@almadar/agent 3.5.6 → 3.5.11
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/dist/agent/index.js +5 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/gates/agentic/tools.d.ts +16 -6
- package/dist/gates/behavior-gates/behavior-orchestrator.d.ts +15 -0
- package/dist/gates/behavior-gates/behavior-prompts.d.ts +10 -0
- package/dist/gates/behavior-gates/bg0-behavior-selection.d.ts +11 -0
- package/dist/gates/behavior-gates/bg0a-decompose.d.ts +29 -0
- package/dist/gates/behavior-gates/bg0b-match.d.ts +15 -0
- package/dist/gates/behavior-gates/bg1-instantiation.d.ts +11 -0
- package/dist/gates/behavior-gates/bg2-wiring.d.ts +11 -0
- package/dist/gates/behavior-gates/bg3-composition.d.ts +12 -0
- package/dist/gates/behavior-gates/index.d.ts +16 -0
- package/dist/gates/behavior-gates/types.d.ts +53 -0
- package/dist/gates/gate35-pattern-match.d.ts +23 -0
- package/dist/gates/gate4-render-ui.d.ts +1 -1
- package/dist/gates/index.js +486 -154
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/prompts.d.ts +1 -1
- package/dist/gates/types.d.ts +3 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/orbitals/batch/prompt-assembler.d.ts +2 -1
- package/dist/tools/index.js +5 -2
- package/dist/tools/index.js.map +1 -1
- package/package.json +2 -2
package/dist/gates/index.js
CHANGED
|
@@ -1,99 +1,16 @@
|
|
|
1
|
-
import { extractJsonFromText, createOpenRouterClient, createDeepSeekClient } from '@almadar/llm';
|
|
2
|
-
import { loadGoldenOrb, getAllBehaviors } from '@almadar/std';
|
|
3
1
|
import fs3 from 'fs';
|
|
4
2
|
import path3 from 'path';
|
|
3
|
+
import { extractJsonFromText, createAnthropicClient, createOpenRouterClient, createDeepSeekClient } from '@almadar/llm';
|
|
5
4
|
import { execSync } from 'child_process';
|
|
6
|
-
import { getDecompositionCompact, getBindingsCompact, getBindingContextRules, getOrbRenderUIGuide } from '@almadar/skills';
|
|
5
|
+
import { getDecompositionCompact, getBindingsCompact, getBindingContextRules, getOrbRenderUIGuideFiltered, getOrbRenderUIGuide } from '@almadar/skills';
|
|
6
|
+
import { loadGoldenOrb, getAllBehaviors } from '@almadar/std';
|
|
7
|
+
import { getOrbAllowedPatterns } from '@almadar/patterns';
|
|
7
8
|
import { tool } from '@langchain/core/tools';
|
|
8
9
|
import { z } from 'zod';
|
|
10
|
+
import * as stdBehaviorFunctions from '@almadar/std/behaviors/functions';
|
|
11
|
+
import { composeBehaviors } from '@almadar/core/builders';
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
-
var __esm = (fn, res) => function __init() {
|
|
13
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
14
|
-
};
|
|
15
|
-
var __export = (target, all) => {
|
|
16
|
-
for (var name in all)
|
|
17
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// src/gates/gate05-behavior-match.ts
|
|
21
|
-
var gate05_behavior_match_exports = {};
|
|
22
|
-
__export(gate05_behavior_match_exports, {
|
|
23
|
-
loadGoldenOrbByName: () => loadGoldenOrbByName,
|
|
24
|
-
runGate05: () => runGate05
|
|
25
|
-
});
|
|
26
|
-
function getBehaviorCatalog() {
|
|
27
|
-
if (cachedCatalog) return cachedCatalog;
|
|
28
|
-
const all = getAllBehaviors();
|
|
29
|
-
const lines = all.map((b) => {
|
|
30
|
-
const name = String(b.name || "");
|
|
31
|
-
const desc = String(b.description || "");
|
|
32
|
-
const states = Array.isArray(b.states) ? b.states.length : 0;
|
|
33
|
-
const events = Array.isArray(b.events) ? b.events.length : 0;
|
|
34
|
-
return `- ${name}: ${desc} (${states} states, ${events} events)`;
|
|
35
|
-
});
|
|
36
|
-
cachedCatalog = lines.join("\n");
|
|
37
|
-
return cachedCatalog;
|
|
38
|
-
}
|
|
39
|
-
function loadGoldenOrbByName(behaviorName) {
|
|
40
|
-
return loadGoldenOrb(behaviorName);
|
|
41
|
-
}
|
|
42
|
-
function buildMatchSystemPrompt() {
|
|
43
|
-
return `You are a behavior matching assistant. Given an application description and its orbital structure, select the most relevant golden behavior from the catalog below.
|
|
44
|
-
|
|
45
|
-
The catalog contains 107 standard behaviors. Each behavior is a proven, production-quality .orb program that handles a specific interaction pattern (CRUD lists, forms, search, cart, booking, etc.).
|
|
46
|
-
|
|
47
|
-
Your task: pick the ONE behavior that best matches the application's primary interaction pattern. If no behavior is a close match, respond with "none".
|
|
48
|
-
|
|
49
|
-
Respond with ONLY valid JSON: { "match": "std-behavior-name" } or { "match": "none" }
|
|
50
|
-
No markdown, no explanation.`;
|
|
51
|
-
}
|
|
52
|
-
function buildMatchUserPrompt(prompt, orb) {
|
|
53
|
-
const orbitalNames = orb.orbitals.map((o) => {
|
|
54
|
-
const name = o.name;
|
|
55
|
-
return name;
|
|
56
|
-
});
|
|
57
|
-
return `Application: ${prompt}
|
|
58
|
-
|
|
59
|
-
Orbitals produced by Gate 0: ${orbitalNames.join(", ")}
|
|
60
|
-
|
|
61
|
-
Behavior catalog:
|
|
62
|
-
${getBehaviorCatalog()}`;
|
|
63
|
-
}
|
|
64
|
-
async function runGate05(client, prompt, orb, opts = {}) {
|
|
65
|
-
const systemPrompt = buildMatchSystemPrompt();
|
|
66
|
-
const userPrompt = buildMatchUserPrompt(prompt, orb);
|
|
67
|
-
const raw = await client.callRaw({
|
|
68
|
-
systemPrompt,
|
|
69
|
-
userPrompt,
|
|
70
|
-
maxTokens: opts.maxTokens ?? 256
|
|
71
|
-
});
|
|
72
|
-
const parsed = extractJsonFromText(raw);
|
|
73
|
-
if (!parsed) {
|
|
74
|
-
console.warn("[Gate 0.5] Failed to parse match response, falling back to pure mode");
|
|
75
|
-
return { matchedName: null, goldenOrb: null };
|
|
76
|
-
}
|
|
77
|
-
const obj = JSON.parse(parsed);
|
|
78
|
-
const matchName = String(obj.match || "none");
|
|
79
|
-
if (matchName === "none") {
|
|
80
|
-
console.log("[Gate 0.5] No behavior match found, using pure mode");
|
|
81
|
-
return { matchedName: null, goldenOrb: null };
|
|
82
|
-
}
|
|
83
|
-
const goldenOrb = loadGoldenOrbByName(matchName);
|
|
84
|
-
if (!goldenOrb) {
|
|
85
|
-
console.warn(`[Gate 0.5] Matched "${matchName}" but could not load .orb file`);
|
|
86
|
-
return { matchedName: matchName, goldenOrb: null };
|
|
87
|
-
}
|
|
88
|
-
console.log(`[Gate 0.5] Matched behavior: ${matchName}`);
|
|
89
|
-
return { matchedName: matchName, goldenOrb };
|
|
90
|
-
}
|
|
91
|
-
var cachedCatalog;
|
|
92
|
-
var init_gate05_behavior_match = __esm({
|
|
93
|
-
"src/gates/gate05-behavior-match.ts"() {
|
|
94
|
-
cachedCatalog = null;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
13
|
+
// src/gates/orchestrator.ts
|
|
97
14
|
var ORBITAL_BINARY = "/home/osamah/bin/orbital";
|
|
98
15
|
function validateWithOrbitalCLI(schema, workDir) {
|
|
99
16
|
if (!fs3.existsSync(ORBITAL_BINARY)) {
|
|
@@ -270,7 +187,7 @@ Your output must be a JSON object with:
|
|
|
270
187
|
- category: "interaction"
|
|
271
188
|
- stateMachine:
|
|
272
189
|
- states: [{ name: camelCase, isInitial?: true }]
|
|
273
|
-
- events: [{ key: "UPPER_SNAKE_CASE", name: "Human Label", payloadSchema
|
|
190
|
+
- events: [{ key: "UPPER_SNAKE_CASE", name: "Human Label", payloadSchema: [{ name, type }] }]
|
|
274
191
|
- transitions: [{ from, event, to, effects: [] }]
|
|
275
192
|
- ui: { "stateName": { "presentation": "inline" | "modal" }, ... }
|
|
276
193
|
|
|
@@ -281,6 +198,26 @@ State Machine Rules:
|
|
|
281
198
|
- The initial state MUST have an INIT self-loop transition (idle->INIT->idle or browsing->INIT->browsing)
|
|
282
199
|
- Leave effects as empty arrays (Gate 3 fills these)
|
|
283
200
|
|
|
201
|
+
Payload Schema Rules (MANDATORY - validation fails without these):
|
|
202
|
+
- EVERY event except INIT, CLOSE, and CANCEL MUST have a payloadSchema. This is NOT optional.
|
|
203
|
+
- SAVE: { "key": "SAVE", "name": "Save", "payloadSchema": [{ "name": "data", "type": "object" }] }
|
|
204
|
+
- EDIT: { "key": "EDIT", "name": "Edit", "payloadSchema": [{ "name": "id", "type": "string" }] }
|
|
205
|
+
- DELETE: { "key": "DELETE", "name": "Delete", "payloadSchema": [{ "name": "id", "type": "string" }] }
|
|
206
|
+
- VIEW: { "key": "VIEW", "name": "View", "payloadSchema": [{ "name": "id", "type": "string" }] }
|
|
207
|
+
- CREATE: { "key": "CREATE", "name": "Create" } (no payload needed, opens empty form)
|
|
208
|
+
- Any event with a verb that acts on an entity needs payloadSchema with at least "id" or "data"
|
|
209
|
+
|
|
210
|
+
Example events array:
|
|
211
|
+
[
|
|
212
|
+
{ "key": "INIT", "name": "Initialize" },
|
|
213
|
+
{ "key": "CREATE", "name": "Create Item" },
|
|
214
|
+
{ "key": "EDIT", "name": "Edit Item", "payloadSchema": [{ "name": "id", "type": "string" }] },
|
|
215
|
+
{ "key": "SAVE", "name": "Save", "payloadSchema": [{ "name": "data", "type": "object" }] },
|
|
216
|
+
{ "key": "DELETE", "name": "Delete Item", "payloadSchema": [{ "name": "id", "type": "string" }] },
|
|
217
|
+
{ "key": "CANCEL", "name": "Cancel" },
|
|
218
|
+
{ "key": "CLOSE", "name": "Close" }
|
|
219
|
+
]
|
|
220
|
+
|
|
284
221
|
UI Slot Assignment Rules:
|
|
285
222
|
- Assign "inline" to primary view states (idle, browsing, listing, viewing)
|
|
286
223
|
- Assign "modal" to overlay action states (creating, editing, deleting, confirming)
|
|
@@ -345,6 +282,13 @@ Rules:
|
|
|
345
282
|
- set target MUST start with @entity (never @payload)
|
|
346
283
|
- Do NOT include render-ui effects (Gate 4 handles those)
|
|
347
284
|
|
|
285
|
+
Guard Rules (CRITICAL):
|
|
286
|
+
- Do NOT wrap a single condition in "and". Use "and" ONLY for 2+ conditions.
|
|
287
|
+
WRONG: ["and", ["eq", "@entity.status", "active"]]
|
|
288
|
+
RIGHT: ["eq", "@entity.status", "active"]
|
|
289
|
+
- ONLY reference fields that exist on the entity shown in the user prompt. Do NOT invent fields.
|
|
290
|
+
If the entity has fields [id, name, status], you can use @entity.name, @entity.status. Do NOT use @entity.items or @entity.data if they are not listed.
|
|
291
|
+
|
|
348
292
|
Respond with ONLY valid JSON. No markdown, no explanation.`;
|
|
349
293
|
}
|
|
350
294
|
function buildGate3UserPrompt(transition, entity, sm, guidedTransition) {
|
|
@@ -362,8 +306,8 @@ ${JSON.stringify(guidedTransition, null, 2)}`;
|
|
|
362
306
|
}
|
|
363
307
|
return userPrompt;
|
|
364
308
|
}
|
|
365
|
-
function buildGate4SystemPrompt() {
|
|
366
|
-
const orbGuide = getOrbRenderUIGuide();
|
|
309
|
+
function buildGate4SystemPrompt(matchedPatterns) {
|
|
310
|
+
const orbGuide = matchedPatterns ? getOrbRenderUIGuideFiltered(matchedPatterns) : getOrbRenderUIGuide();
|
|
367
311
|
return `You are a UI designer. Given a transition with its effects, produce the render-ui pattern tree.
|
|
368
312
|
|
|
369
313
|
${orbGuide}
|
|
@@ -375,19 +319,27 @@ Your output must be a JSON object with:
|
|
|
375
319
|
|
|
376
320
|
Slot Rules:
|
|
377
321
|
- Use the slot shown in the user prompt for the target state (main or modal)
|
|
378
|
-
- When exiting a modal state (the user prompt will say so), you MUST include:
|
|
322
|
+
- When exiting a modal state (the user prompt will say so), you MUST include BOTH:
|
|
379
323
|
1. { "slot": "modal", "tree": null } to clear the modal
|
|
380
|
-
2. { "slot": "main", "tree": { ... } } to render the main view
|
|
324
|
+
2. { "slot": "main", "tree": { ... } } to re-render the main view
|
|
325
|
+
WITHOUT BOTH, the main view shows stale content after closing the modal. This is a validation error.
|
|
381
326
|
|
|
382
327
|
Binding Rules:
|
|
383
328
|
- Bindings are ONLY paths: @entity.name, @entity.price (NO expressions like @entity.x <= 0)
|
|
384
329
|
- Each prop value must contain at most ONE binding. NEVER concatenate: "@entity.price @entity.currency" is INVALID
|
|
385
330
|
- For combined display, use separate typography elements or static labels
|
|
386
331
|
- button event props must match state machine event keys
|
|
332
|
+
- ONLY bind to entity fields shown in the user prompt. Do NOT invent fields.
|
|
333
|
+
If entity has fields [id, name, status, price], you can use @entity.name, @entity.price.
|
|
334
|
+
Do NOT use @entity.items, @entity.data, or any field not listed. This is a validation error.
|
|
387
335
|
|
|
388
336
|
Pattern Rules:
|
|
389
337
|
- Use ONLY props that exist on the pattern. Do NOT invent props.
|
|
390
|
-
- data-list:
|
|
338
|
+
- data-list fields MUST be a string array: ["name", "status", "price"]. NEVER use objects or nested trees for fields.
|
|
339
|
+
WRONG: "fields": [{ "type": "stack", "children": [...] }]
|
|
340
|
+
WRONG: "fields": [{ "field": "name", "label": "Name" }]
|
|
341
|
+
RIGHT: "fields": ["name", "status", "price"]
|
|
342
|
+
- data-list: entity, fields, itemActions (NOT emptyIcon, emptyTitle)
|
|
391
343
|
- button: label, variant, event, icon, size (NOT disabled)
|
|
392
344
|
- card: children (NOT padding, bg, border, use box for those)
|
|
393
345
|
|
|
@@ -519,9 +471,72 @@ async function runGate0(client, prompt, opts = {}, behaviorData) {
|
|
|
519
471
|
});
|
|
520
472
|
return parseGate0Response(raw);
|
|
521
473
|
}
|
|
474
|
+
var cachedCatalog = null;
|
|
475
|
+
function getBehaviorCatalog() {
|
|
476
|
+
if (cachedCatalog) return cachedCatalog;
|
|
477
|
+
const all = getAllBehaviors();
|
|
478
|
+
const lines = all.map((b) => {
|
|
479
|
+
const name = String(b.name || "");
|
|
480
|
+
const desc = String(b.description || "");
|
|
481
|
+
const states = Array.isArray(b.states) ? b.states.length : 0;
|
|
482
|
+
const events = Array.isArray(b.events) ? b.events.length : 0;
|
|
483
|
+
return `- ${name}: ${desc} (${states} states, ${events} events)`;
|
|
484
|
+
});
|
|
485
|
+
cachedCatalog = lines.join("\n");
|
|
486
|
+
return cachedCatalog;
|
|
487
|
+
}
|
|
488
|
+
function loadGoldenOrbByName(behaviorName) {
|
|
489
|
+
return loadGoldenOrb(behaviorName);
|
|
490
|
+
}
|
|
491
|
+
function buildMatchSystemPrompt() {
|
|
492
|
+
return `You are a behavior matching assistant. Given an application description and its orbital structure, select the most relevant golden behavior from the catalog below.
|
|
522
493
|
|
|
523
|
-
|
|
524
|
-
|
|
494
|
+
The catalog contains 107 standard behaviors. Each behavior is a proven, production-quality .orb program that handles a specific interaction pattern (CRUD lists, forms, search, cart, booking, etc.).
|
|
495
|
+
|
|
496
|
+
Your task: pick the ONE behavior that best matches the application's primary interaction pattern. If no behavior is a close match, respond with "none".
|
|
497
|
+
|
|
498
|
+
Respond with ONLY valid JSON: { "match": "std-behavior-name" } or { "match": "none" }
|
|
499
|
+
No markdown, no explanation.`;
|
|
500
|
+
}
|
|
501
|
+
function buildMatchUserPrompt(prompt, orb) {
|
|
502
|
+
const orbitalNames = orb.orbitals.map((o) => {
|
|
503
|
+
const name = o.name;
|
|
504
|
+
return name;
|
|
505
|
+
});
|
|
506
|
+
return `Application: ${prompt}
|
|
507
|
+
|
|
508
|
+
Orbitals produced by Gate 0: ${orbitalNames.join(", ")}
|
|
509
|
+
|
|
510
|
+
Behavior catalog:
|
|
511
|
+
${getBehaviorCatalog()}`;
|
|
512
|
+
}
|
|
513
|
+
async function runGate05(client, prompt, orb, opts = {}) {
|
|
514
|
+
const systemPrompt = buildMatchSystemPrompt();
|
|
515
|
+
const userPrompt = buildMatchUserPrompt(prompt, orb);
|
|
516
|
+
const raw = await client.callRaw({
|
|
517
|
+
systemPrompt,
|
|
518
|
+
userPrompt,
|
|
519
|
+
maxTokens: opts.maxTokens ?? 256
|
|
520
|
+
});
|
|
521
|
+
const parsed = extractJsonFromText(raw);
|
|
522
|
+
if (!parsed) {
|
|
523
|
+
console.warn("[Gate 0.5] Failed to parse match response, falling back to pure mode");
|
|
524
|
+
return { matchedName: null, goldenOrb: null };
|
|
525
|
+
}
|
|
526
|
+
const obj = JSON.parse(parsed);
|
|
527
|
+
const matchName = String(obj.match || "none");
|
|
528
|
+
if (matchName === "none") {
|
|
529
|
+
console.log("[Gate 0.5] No behavior match found, using pure mode");
|
|
530
|
+
return { matchedName: null, goldenOrb: null };
|
|
531
|
+
}
|
|
532
|
+
const goldenOrb = loadGoldenOrbByName(matchName);
|
|
533
|
+
if (!goldenOrb) {
|
|
534
|
+
console.warn(`[Gate 0.5] Matched "${matchName}" but could not load .orb file`);
|
|
535
|
+
return { matchedName: matchName, goldenOrb: null };
|
|
536
|
+
}
|
|
537
|
+
console.log(`[Gate 0.5] Matched behavior: ${matchName}`);
|
|
538
|
+
return { matchedName: matchName, goldenOrb };
|
|
539
|
+
}
|
|
525
540
|
|
|
526
541
|
// src/gates/merge.ts
|
|
527
542
|
function deepCloneOrb(orb) {
|
|
@@ -823,6 +838,296 @@ function findGuidedTransition(goldenOrb, traitName, from, event) {
|
|
|
823
838
|
}
|
|
824
839
|
return void 0;
|
|
825
840
|
}
|
|
841
|
+
var CORE_PATTERNS = [
|
|
842
|
+
"stack",
|
|
843
|
+
"box",
|
|
844
|
+
"typography",
|
|
845
|
+
"divider",
|
|
846
|
+
"icon",
|
|
847
|
+
"button"
|
|
848
|
+
];
|
|
849
|
+
var GAME_EVENTS = /* @__PURE__ */ new Set([
|
|
850
|
+
"ATTACK",
|
|
851
|
+
"DEFEND",
|
|
852
|
+
"MOVE",
|
|
853
|
+
"SPAWN",
|
|
854
|
+
"DAMAGE",
|
|
855
|
+
"HEAL",
|
|
856
|
+
"CAST",
|
|
857
|
+
"LEVEL_UP",
|
|
858
|
+
"GAME_OVER",
|
|
859
|
+
"START_GAME",
|
|
860
|
+
"PAUSE",
|
|
861
|
+
"RESUME",
|
|
862
|
+
"NEXT_TURN",
|
|
863
|
+
"END_TURN",
|
|
864
|
+
"COLLECT",
|
|
865
|
+
"EQUIP",
|
|
866
|
+
"UNEQUIP",
|
|
867
|
+
"CRAFT",
|
|
868
|
+
"TICK",
|
|
869
|
+
"STEP",
|
|
870
|
+
"SIMULATE"
|
|
871
|
+
]);
|
|
872
|
+
var GAME_PATTERN_NAMES = /* @__PURE__ */ new Set([
|
|
873
|
+
"health-bar",
|
|
874
|
+
"score-display",
|
|
875
|
+
"control-button",
|
|
876
|
+
"d-pad",
|
|
877
|
+
"action-button",
|
|
878
|
+
"choice-button",
|
|
879
|
+
"combo-counter",
|
|
880
|
+
"damage-number",
|
|
881
|
+
"mini-map",
|
|
882
|
+
"resource-counter",
|
|
883
|
+
"timer-display",
|
|
884
|
+
"turn-indicator",
|
|
885
|
+
"waypoint-marker",
|
|
886
|
+
"x-p-bar",
|
|
887
|
+
"crafting-recipe",
|
|
888
|
+
"enemy-plate",
|
|
889
|
+
"health-panel",
|
|
890
|
+
"quest-tracker",
|
|
891
|
+
"resource-bar",
|
|
892
|
+
"turn-panel",
|
|
893
|
+
"unit-command-bar",
|
|
894
|
+
"powerup-slots",
|
|
895
|
+
"item-slot",
|
|
896
|
+
"status-effect",
|
|
897
|
+
// display: game/simulation
|
|
898
|
+
"model-loader",
|
|
899
|
+
"feature-renderer",
|
|
900
|
+
"unit-renderer",
|
|
901
|
+
"state-indicator",
|
|
902
|
+
"simulation-controls",
|
|
903
|
+
"simulation-graph",
|
|
904
|
+
"event-log",
|
|
905
|
+
"object-rule-panel",
|
|
906
|
+
"rule-editor",
|
|
907
|
+
"action-palette",
|
|
908
|
+
"sequence-bar",
|
|
909
|
+
"state-node",
|
|
910
|
+
"transition-arrow",
|
|
911
|
+
"variable-panel"
|
|
912
|
+
]);
|
|
913
|
+
var SEARCH_PATTERNS = /* @__PURE__ */ new Set(["search-input"]);
|
|
914
|
+
var WIZARD_PATTERNS = /* @__PURE__ */ new Set(["wizard-progress", "wizard-navigation"]);
|
|
915
|
+
var SPECIALIZED_PATTERNS = /* @__PURE__ */ new Set([
|
|
916
|
+
"law-reference-tooltip",
|
|
917
|
+
"violation-alert",
|
|
918
|
+
"scaled-diagram",
|
|
919
|
+
"quiz-block",
|
|
920
|
+
"code-block",
|
|
921
|
+
"code-view",
|
|
922
|
+
"markdown-content",
|
|
923
|
+
"flip-card",
|
|
924
|
+
"flip-container",
|
|
925
|
+
"confetti-effect",
|
|
926
|
+
"typewriter-text",
|
|
927
|
+
"pull-to-refresh",
|
|
928
|
+
"swipeable-row",
|
|
929
|
+
"lightbox",
|
|
930
|
+
"carousel",
|
|
931
|
+
"calendar-grid",
|
|
932
|
+
"time-slot-cell",
|
|
933
|
+
"day-cell",
|
|
934
|
+
"line-chart",
|
|
935
|
+
"chart-legend",
|
|
936
|
+
"graph-view",
|
|
937
|
+
"split-pane",
|
|
938
|
+
"master-detail",
|
|
939
|
+
"tabbed-container",
|
|
940
|
+
// Aliases (typography already covers these)
|
|
941
|
+
"heading",
|
|
942
|
+
"text",
|
|
943
|
+
// Alias for button
|
|
944
|
+
"button-pattern",
|
|
945
|
+
// Niche/unlikely for most apps
|
|
946
|
+
"conditional-wrapper",
|
|
947
|
+
"text-highlight",
|
|
948
|
+
"theme-toggle",
|
|
949
|
+
"flex",
|
|
950
|
+
"floating-action-button",
|
|
951
|
+
"animated-counter",
|
|
952
|
+
"infinite-scroll-sentinel",
|
|
953
|
+
"progress-dots",
|
|
954
|
+
"map-view",
|
|
955
|
+
"range-slider",
|
|
956
|
+
"number-stepper",
|
|
957
|
+
"star-rating",
|
|
958
|
+
"upload-drop-zone",
|
|
959
|
+
"collapsible-section",
|
|
960
|
+
"stat-badge",
|
|
961
|
+
"status-dot",
|
|
962
|
+
"sortable-list",
|
|
963
|
+
"trend-indicator",
|
|
964
|
+
// Container patterns (compiler handles these, not render-ui)
|
|
965
|
+
"modal",
|
|
966
|
+
"drawer",
|
|
967
|
+
"overlay",
|
|
968
|
+
// Form sub-patterns (form-section is enough)
|
|
969
|
+
"repeatable-form-section",
|
|
970
|
+
"form-field",
|
|
971
|
+
"form-section-header",
|
|
972
|
+
"input-group",
|
|
973
|
+
"relation-select",
|
|
974
|
+
"date-range-selector",
|
|
975
|
+
// Layout extras (stack/box cover these)
|
|
976
|
+
"grid",
|
|
977
|
+
"center",
|
|
978
|
+
"spacer",
|
|
979
|
+
// Error boundary (runtime handles this)
|
|
980
|
+
"error-boundary"
|
|
981
|
+
]);
|
|
982
|
+
function analyzeApp(orb) {
|
|
983
|
+
const signals = {
|
|
984
|
+
hasModalStates: false,
|
|
985
|
+
hasInlineStates: false,
|
|
986
|
+
hasCreateEdit: false,
|
|
987
|
+
hasDelete: false,
|
|
988
|
+
hasSearch: false,
|
|
989
|
+
hasWizard: false,
|
|
990
|
+
isGame: false,
|
|
991
|
+
eventNames: /* @__PURE__ */ new Set()
|
|
992
|
+
};
|
|
993
|
+
for (const orbital of orb.orbitals) {
|
|
994
|
+
for (const traitRef of orbital.traits) {
|
|
995
|
+
if (typeof traitRef === "string" || "ref" in traitRef) continue;
|
|
996
|
+
const trait = traitRef;
|
|
997
|
+
if (!trait.stateMachine) continue;
|
|
998
|
+
const ui = trait.ui;
|
|
999
|
+
if (ui) {
|
|
1000
|
+
for (const val of Object.values(ui)) {
|
|
1001
|
+
if (val.presentation === "modal") signals.hasModalStates = true;
|
|
1002
|
+
if (val.presentation === "inline") signals.hasInlineStates = true;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
for (const state of trait.stateMachine.states) {
|
|
1006
|
+
const name = state.name.toLowerCase();
|
|
1007
|
+
if (name.includes("creat") || name.includes("edit")) signals.hasCreateEdit = true;
|
|
1008
|
+
if (name.includes("delet") || name.includes("confirm")) signals.hasDelete = true;
|
|
1009
|
+
if (name.includes("search") || name.includes("filter")) signals.hasSearch = true;
|
|
1010
|
+
if (name.includes("step") || name.includes("wizard")) signals.hasWizard = true;
|
|
1011
|
+
}
|
|
1012
|
+
for (const event of trait.stateMachine.events) {
|
|
1013
|
+
signals.eventNames.add(event.key);
|
|
1014
|
+
if (GAME_EVENTS.has(event.key)) signals.isGame = true;
|
|
1015
|
+
if (event.key === "SEARCH" || event.key === "FILTER") signals.hasSearch = true;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return signals;
|
|
1020
|
+
}
|
|
1021
|
+
function preFilterPatterns(signals) {
|
|
1022
|
+
const grouped = getOrbAllowedPatterns();
|
|
1023
|
+
const allowed = [];
|
|
1024
|
+
for (const items of Object.values(grouped)) {
|
|
1025
|
+
for (const item of items) {
|
|
1026
|
+
if (CORE_PATTERNS.includes(item.name)) continue;
|
|
1027
|
+
if (!signals.isGame && GAME_PATTERN_NAMES.has(item.name)) continue;
|
|
1028
|
+
if (!signals.hasSearch && SEARCH_PATTERNS.has(item.name)) continue;
|
|
1029
|
+
if (!signals.hasWizard && WIZARD_PATTERNS.has(item.name)) continue;
|
|
1030
|
+
if (!signals.isGame && SPECIALIZED_PATTERNS.has(item.name)) continue;
|
|
1031
|
+
allowed.push(item.name);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return allowed;
|
|
1035
|
+
}
|
|
1036
|
+
function buildSlimCatalog(patternNames) {
|
|
1037
|
+
const grouped = getOrbAllowedPatterns();
|
|
1038
|
+
const nameSet = new Set(patternNames);
|
|
1039
|
+
const lines = [];
|
|
1040
|
+
for (const [cat, items] of Object.entries(grouped).sort()) {
|
|
1041
|
+
const filtered = items.filter((i) => nameSet.has(i.name));
|
|
1042
|
+
if (filtered.length === 0) continue;
|
|
1043
|
+
lines.push(`## ${cat}`);
|
|
1044
|
+
for (const item of filtered) {
|
|
1045
|
+
const desc = item.description.split("\n")[0].slice(0, 80);
|
|
1046
|
+
lines.push(`- ${item.name}: ${desc}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return lines.join("\n");
|
|
1050
|
+
}
|
|
1051
|
+
function buildSystemPrompt(preFiltered, signals) {
|
|
1052
|
+
const catalog = buildSlimCatalog(preFiltered);
|
|
1053
|
+
return `You are a UI pattern selector. Pick 5-15 patterns from the catalog below to render this application's UI.
|
|
1054
|
+
|
|
1055
|
+
Core layout patterns are ALWAYS included (do not list them):
|
|
1056
|
+
${CORE_PATTERNS.join(", ")}
|
|
1057
|
+
|
|
1058
|
+
Select from each applicable category:
|
|
1059
|
+
|
|
1060
|
+
**Content (pick 1-3)**: data-list or data-grid for lists, card for grouping, stat-display for metrics, badge for status
|
|
1061
|
+
**Forms${signals.hasCreateEdit ? " (REQUIRED - app has create/edit states)" : ""}**: form-section for create/edit forms
|
|
1062
|
+
**Feedback (pick 1-2)**: empty-state, loading-state, error-state, notification
|
|
1063
|
+
**Navigation (if needed)**: tabs, pagination, breadcrumb
|
|
1064
|
+
|
|
1065
|
+
Pick at most 15 patterns total. Only pick patterns you are confident the app needs.
|
|
1066
|
+
|
|
1067
|
+
Catalog (${preFiltered.length} patterns):
|
|
1068
|
+
${catalog}
|
|
1069
|
+
|
|
1070
|
+
Respond with ONLY valid JSON: { "patterns": ["pattern-name", ...] }`;
|
|
1071
|
+
}
|
|
1072
|
+
function buildUserPrompt(orb) {
|
|
1073
|
+
const lines = [];
|
|
1074
|
+
for (const orbital of orb.orbitals) {
|
|
1075
|
+
const entity = orbital.entity;
|
|
1076
|
+
const fields = entity.fields?.map((f) => `${f.name}:${f.type}`).join(", ") || "none";
|
|
1077
|
+
lines.push(`Entity: ${entity.name} (${fields})`);
|
|
1078
|
+
for (const traitRef of orbital.traits) {
|
|
1079
|
+
if (typeof traitRef === "string" || "ref" in traitRef) continue;
|
|
1080
|
+
const trait = traitRef;
|
|
1081
|
+
if (!trait.stateMachine) continue;
|
|
1082
|
+
const states = trait.stateMachine.states.map((s) => s.name).join(", ");
|
|
1083
|
+
const events = trait.stateMachine.events.map((e) => e.key).join(", ");
|
|
1084
|
+
const ui = trait.ui;
|
|
1085
|
+
const slots = ui ? Object.entries(ui).map(([s, v]) => `${s}(${v.presentation})`).join(", ") : "none";
|
|
1086
|
+
lines.push(`Trait: ${trait.name} | States: ${states} | Events: ${events} | Slots: ${slots}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return `Select UI patterns for this application:
|
|
1090
|
+
|
|
1091
|
+
${lines.join("\n")}`;
|
|
1092
|
+
}
|
|
1093
|
+
async function runGate35(client, orb, _opts = {}) {
|
|
1094
|
+
const signals = analyzeApp(orb);
|
|
1095
|
+
const preFiltered = preFilterPatterns(signals);
|
|
1096
|
+
console.log(`[Gate 3.5] Pre-filtered: ${preFiltered.length} patterns (game=${signals.isGame}, createEdit=${signals.hasCreateEdit})`);
|
|
1097
|
+
const systemPrompt = buildSystemPrompt(preFiltered, signals);
|
|
1098
|
+
const userPrompt = buildUserPrompt(orb);
|
|
1099
|
+
try {
|
|
1100
|
+
const raw = await client.callRaw({
|
|
1101
|
+
systemPrompt,
|
|
1102
|
+
userPrompt,
|
|
1103
|
+
maxTokens: 256
|
|
1104
|
+
});
|
|
1105
|
+
const jsonStr = extractJsonFromText(raw);
|
|
1106
|
+
if (!jsonStr) {
|
|
1107
|
+
console.warn("[Gate 3.5] No JSON in response, using pre-filtered set");
|
|
1108
|
+
return [.../* @__PURE__ */ new Set([...CORE_PATTERNS, ...preFiltered])];
|
|
1109
|
+
}
|
|
1110
|
+
const parsed = JSON.parse(jsonStr);
|
|
1111
|
+
const selected = Array.isArray(parsed.patterns) ? parsed.patterns : [];
|
|
1112
|
+
const preFilteredSet = new Set(preFiltered);
|
|
1113
|
+
const validated = selected.filter((p) => preFilteredSet.has(p));
|
|
1114
|
+
const result = [.../* @__PURE__ */ new Set([...CORE_PATTERNS, ...validated])];
|
|
1115
|
+
if (result.length > 20) {
|
|
1116
|
+
return [...CORE_PATTERNS, ...validated.slice(0, 14)];
|
|
1117
|
+
}
|
|
1118
|
+
if (result.length < 8) {
|
|
1119
|
+
console.warn(`[Gate 3.5] Only ${result.length} patterns, adding essentials`);
|
|
1120
|
+
const essentials = ["badge", "stat-display", "data-list", "form-section", "empty-state", "card"];
|
|
1121
|
+
for (const p of essentials) {
|
|
1122
|
+
if (!result.includes(p) && preFilteredSet.has(p)) result.push(p);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
return result;
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
console.warn(`[Gate 3.5] Error: ${error instanceof Error ? error.message : error}, using pre-filtered set`);
|
|
1128
|
+
return [.../* @__PURE__ */ new Set([...CORE_PATTERNS, ...preFiltered])];
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
826
1131
|
function buildSlotContext(sm, traitUi) {
|
|
827
1132
|
const slots = {};
|
|
828
1133
|
for (const state of sm.states) {
|
|
@@ -850,8 +1155,8 @@ function parseGate4Response(raw) {
|
|
|
850
1155
|
tree: r.tree === null ? null : r.tree || { type: "typography", variant: "body", content: "Empty" }
|
|
851
1156
|
}));
|
|
852
1157
|
}
|
|
853
|
-
async function runGate4(client, orb, opts = {}, _behaviorData) {
|
|
854
|
-
const systemPrompt = buildGate4SystemPrompt();
|
|
1158
|
+
async function runGate4(client, orb, opts = {}, _behaviorData, matchedPatterns) {
|
|
1159
|
+
const systemPrompt = buildGate4SystemPrompt(matchedPatterns);
|
|
855
1160
|
let result = orb;
|
|
856
1161
|
for (const orbital of orb.orbitals) {
|
|
857
1162
|
const entity = orbital.entity;
|
|
@@ -968,6 +1273,9 @@ function createClientForProvider(config) {
|
|
|
968
1273
|
case "mistral-medium":
|
|
969
1274
|
client = createOpenRouterClient({ model: "mistralai/mistral-medium-3.1", ...opts });
|
|
970
1275
|
break;
|
|
1276
|
+
case "anthropic":
|
|
1277
|
+
client = createAnthropicClient(opts);
|
|
1278
|
+
break;
|
|
971
1279
|
default: {
|
|
972
1280
|
const _exhaustive = config.provider;
|
|
973
1281
|
throw new Error(`Unknown provider: ${_exhaustive}`);
|
|
@@ -986,8 +1294,29 @@ ${opts2.userPrompt}` });
|
|
|
986
1294
|
function timeMs() {
|
|
987
1295
|
return performance.now();
|
|
988
1296
|
}
|
|
1297
|
+
function withCallCounter(client) {
|
|
1298
|
+
let calls = 0;
|
|
1299
|
+
const proxy = new Proxy(client, {
|
|
1300
|
+
get(target, prop, receiver) {
|
|
1301
|
+
if (prop === "calls") return calls;
|
|
1302
|
+
if (prop === "reset") return () => {
|
|
1303
|
+
calls = 0;
|
|
1304
|
+
};
|
|
1305
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1306
|
+
if (prop === "callRaw" || prop === "callRawWithMetadata") {
|
|
1307
|
+
return (...args) => {
|
|
1308
|
+
calls++;
|
|
1309
|
+
return value.apply(target, args);
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
return value;
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
return proxy;
|
|
1316
|
+
}
|
|
989
1317
|
async function runGatePipeline(prompt, config, behaviorData, goldenOrb) {
|
|
990
|
-
const
|
|
1318
|
+
const counter = withCallCounter(createClientForProvider(config));
|
|
1319
|
+
const client = counter;
|
|
991
1320
|
const maxTokens = config.maxTokens || 4096;
|
|
992
1321
|
const pipelineStart = timeMs();
|
|
993
1322
|
const opts = {
|
|
@@ -995,41 +1324,60 @@ async function runGatePipeline(prompt, config, behaviorData, goldenOrb) {
|
|
|
995
1324
|
goldenOrb,
|
|
996
1325
|
workDir: config.workDir
|
|
997
1326
|
};
|
|
1327
|
+
counter.reset();
|
|
998
1328
|
const g0Start = timeMs();
|
|
999
1329
|
const orbAfterGate0 = await runGate0(client, prompt, opts, behaviorData);
|
|
1000
1330
|
const g0Ms = timeMs() - g0Start;
|
|
1001
|
-
|
|
1331
|
+
const g0Calls = counter.calls;
|
|
1332
|
+
console.log(`[Gate 0] ${orbAfterGate0.name}: ${orbAfterGate0.orbitals.length} orbital(s) (${g0Ms.toFixed(0)}ms, ${g0Calls} LLM call(s))`);
|
|
1002
1333
|
maybeValidate(config, orbAfterGate0, 0);
|
|
1334
|
+
let g05Calls = 0;
|
|
1003
1335
|
if (config.mode === "guided" && !goldenOrb) {
|
|
1336
|
+
counter.reset();
|
|
1004
1337
|
const g05Start = timeMs();
|
|
1005
1338
|
const matchResult = await runGate05(client, prompt, orbAfterGate0, opts);
|
|
1006
1339
|
const g05Ms = timeMs() - g05Start;
|
|
1340
|
+
g05Calls = counter.calls;
|
|
1007
1341
|
if (matchResult.goldenOrb) {
|
|
1008
1342
|
opts.goldenOrb = matchResult.goldenOrb;
|
|
1009
|
-
console.log(`[Gate 0.5] Matched: ${matchResult.matchedName} (${g05Ms.toFixed(0)}ms)`);
|
|
1343
|
+
console.log(`[Gate 0.5] Matched: ${matchResult.matchedName} (${g05Ms.toFixed(0)}ms, ${g05Calls} LLM call(s))`);
|
|
1010
1344
|
} else {
|
|
1011
|
-
console.log(`[Gate 0.5] No match, continuing in pure mode (${g05Ms.toFixed(0)}ms)`);
|
|
1345
|
+
console.log(`[Gate 0.5] No match, continuing in pure mode (${g05Ms.toFixed(0)}ms, ${g05Calls} LLM call(s))`);
|
|
1012
1346
|
}
|
|
1013
1347
|
}
|
|
1348
|
+
counter.reset();
|
|
1014
1349
|
const g1Start = timeMs();
|
|
1015
1350
|
const orbAfterGate1 = await runGate1(client, orbAfterGate0, opts);
|
|
1016
1351
|
const g1Ms = timeMs() - g1Start;
|
|
1017
|
-
|
|
1352
|
+
const g1Calls = counter.calls;
|
|
1353
|
+
console.log(`[Gate 1] Enriched ${orbAfterGate1.orbitals.length} orbital(s) (${g1Ms.toFixed(0)}ms, ${g1Calls} LLM call(s))`);
|
|
1018
1354
|
maybeValidate(config, orbAfterGate1, 1);
|
|
1355
|
+
counter.reset();
|
|
1019
1356
|
const g2Start = timeMs();
|
|
1020
1357
|
const orbAfterGate2 = await runGate2(client, orbAfterGate1, opts);
|
|
1021
1358
|
const g2Ms = timeMs() - g2Start;
|
|
1022
|
-
|
|
1359
|
+
const g2Calls = counter.calls;
|
|
1360
|
+
console.log(`[Gate 2] State machines filled (${g2Ms.toFixed(0)}ms, ${g2Calls} LLM call(s))`);
|
|
1023
1361
|
maybeValidate(config, orbAfterGate2, 2);
|
|
1362
|
+
counter.reset();
|
|
1024
1363
|
const g3Start = timeMs();
|
|
1025
1364
|
const orbAfterGate3 = await runGate3(client, orbAfterGate2, opts);
|
|
1026
1365
|
const g3Ms = timeMs() - g3Start;
|
|
1027
|
-
|
|
1366
|
+
const g3Calls = counter.calls;
|
|
1367
|
+
console.log(`[Gate 3] Guards + effects filled (${g3Ms.toFixed(0)}ms, ${g3Calls} LLM call(s))`);
|
|
1028
1368
|
maybeValidate(config, orbAfterGate3, 3);
|
|
1369
|
+
counter.reset();
|
|
1370
|
+
const g35Start = timeMs();
|
|
1371
|
+
const matchedPatterns = await runGate35(client, orbAfterGate3, opts);
|
|
1372
|
+
const g35Ms = timeMs() - g35Start;
|
|
1373
|
+
const g35Calls = counter.calls;
|
|
1374
|
+
console.log(`[Gate 3.5] Matched ${matchedPatterns.length} patterns (${g35Ms.toFixed(0)}ms, ${g35Calls} LLM call(s))`);
|
|
1375
|
+
counter.reset();
|
|
1029
1376
|
const g4Start = timeMs();
|
|
1030
|
-
const orbFinal = await runGate4(client, orbAfterGate3, opts);
|
|
1377
|
+
const orbFinal = await runGate4(client, orbAfterGate3, opts, behaviorData, matchedPatterns);
|
|
1031
1378
|
const g4Ms = timeMs() - g4Start;
|
|
1032
|
-
|
|
1379
|
+
const g4Calls = counter.calls;
|
|
1380
|
+
console.log(`[Gate 4] Render-ui appended (${g4Ms.toFixed(0)}ms, ${g4Calls} LLM call(s))`);
|
|
1033
1381
|
fs3.mkdirSync(config.workDir, { recursive: true });
|
|
1034
1382
|
const validation = validateWithOrbitalCLI(orbFinal, config.workDir);
|
|
1035
1383
|
console.log(`[Validate] valid=${validation.valid}, errors=${(validation.errors || []).length}`);
|
|
@@ -1038,12 +1386,14 @@ async function runGatePipeline(prompt, config, behaviorData, goldenOrb) {
|
|
|
1038
1386
|
verify = await runVerify(orbFinal, config);
|
|
1039
1387
|
}
|
|
1040
1388
|
const totalMs = timeMs() - pipelineStart;
|
|
1041
|
-
|
|
1389
|
+
const totalCalls = g0Calls + g05Calls + g1Calls + g2Calls + g3Calls + g35Calls + g4Calls;
|
|
1390
|
+
console.log(`[Pipeline] Total: ${(totalMs / 1e3).toFixed(1)}s, ${totalCalls} LLM calls`);
|
|
1042
1391
|
const timings = {
|
|
1043
1392
|
gate0Ms: g0Ms,
|
|
1044
1393
|
gate1Ms: [g1Ms],
|
|
1045
1394
|
gate2Ms: [g2Ms],
|
|
1046
1395
|
gate3Ms: [g3Ms],
|
|
1396
|
+
gate35Ms: g35Ms,
|
|
1047
1397
|
gate4Ms: [g4Ms],
|
|
1048
1398
|
totalMs
|
|
1049
1399
|
};
|
|
@@ -1109,9 +1459,6 @@ function maybeValidate(config, orb, gate) {
|
|
|
1109
1459
|
}
|
|
1110
1460
|
}
|
|
1111
1461
|
|
|
1112
|
-
// src/gates/index.ts
|
|
1113
|
-
init_gate05_behavior_match();
|
|
1114
|
-
|
|
1115
1462
|
// src/gates/behavior-extract.ts
|
|
1116
1463
|
function asArray(val) {
|
|
1117
1464
|
if (!val) return [];
|
|
@@ -1486,7 +1833,6 @@ function countNodes(tree) {
|
|
|
1486
1833
|
}
|
|
1487
1834
|
return count;
|
|
1488
1835
|
}
|
|
1489
|
-
init_gate05_behavior_match();
|
|
1490
1836
|
function createGateClient(provider = "mistral-small") {
|
|
1491
1837
|
let client;
|
|
1492
1838
|
switch (provider) {
|
|
@@ -1586,8 +1932,7 @@ function createBuildOrbitalTool(workDir, gateProvider) {
|
|
|
1586
1932
|
}
|
|
1587
1933
|
let goldenOrb;
|
|
1588
1934
|
if (input.goldenBehavior) {
|
|
1589
|
-
|
|
1590
|
-
goldenOrb = loadGoldenOrbByName2(input.goldenBehavior) ?? void 0;
|
|
1935
|
+
goldenOrb = loadGoldenOrbByName(input.goldenBehavior) ?? void 0;
|
|
1591
1936
|
}
|
|
1592
1937
|
const opts = {
|
|
1593
1938
|
goldenOrb
|
|
@@ -1696,10 +2041,10 @@ function createVerifyAppTool(workDir) {
|
|
|
1696
2041
|
);
|
|
1697
2042
|
}
|
|
1698
2043
|
var BEHAVIOR_FUNCTIONS = {};
|
|
1699
|
-
|
|
2044
|
+
function loadBehaviorFunctions() {
|
|
1700
2045
|
if (Object.keys(BEHAVIOR_FUNCTIONS).length > 0) return;
|
|
1701
2046
|
try {
|
|
1702
|
-
const fns =
|
|
2047
|
+
const fns = stdBehaviorFunctions;
|
|
1703
2048
|
const mapping = {
|
|
1704
2049
|
// Molecules: CRUD/Data
|
|
1705
2050
|
"std-list": "stdList",
|
|
@@ -1779,7 +2124,7 @@ async function loadBehaviorFunctions() {
|
|
|
1779
2124
|
function createUseBehaviorTool(workDir) {
|
|
1780
2125
|
return tool(
|
|
1781
2126
|
async (input) => {
|
|
1782
|
-
|
|
2127
|
+
loadBehaviorFunctions();
|
|
1783
2128
|
const newEntityName = input.entityName;
|
|
1784
2129
|
const newFields = JSON.parse(input.fieldsJson);
|
|
1785
2130
|
let finalOrbital;
|
|
@@ -1799,7 +2144,6 @@ function createUseBehaviorTool(workDir) {
|
|
|
1799
2144
|
finalOrbital = orbital;
|
|
1800
2145
|
} else {
|
|
1801
2146
|
console.log(`[use_behavior] Falling back to substitution for ${input.behaviorName}`);
|
|
1802
|
-
const { loadGoldenOrb } = await import('@almadar/std');
|
|
1803
2147
|
const behavior = loadGoldenOrb(input.behaviorName);
|
|
1804
2148
|
if (!behavior) {
|
|
1805
2149
|
return { success: false, error: `Behavior "${input.behaviorName}" not found` };
|
|
@@ -1995,43 +2339,29 @@ function createComposeTool(workDir) {
|
|
|
1995
2339
|
if (orbitals.length === 0) {
|
|
1996
2340
|
return { success: false, error: "No orbitals found in .orbitals/" };
|
|
1997
2341
|
}
|
|
1998
|
-
let
|
|
1999
|
-
if (input.
|
|
2000
|
-
|
|
2001
|
-
} else {
|
|
2002
|
-
pages = orbitals.map((o, i) => {
|
|
2003
|
-
const oName = String(o.name || `Orbital${i}`);
|
|
2004
|
-
const entityName = oName.replace(/Orbital$/i, "");
|
|
2005
|
-
const traits = o.traits || [];
|
|
2006
|
-
return {
|
|
2007
|
-
name: `${entityName}Page`,
|
|
2008
|
-
path: `/${entityName.toLowerCase()}s`,
|
|
2009
|
-
...i === 0 ? { isInitial: true } : {},
|
|
2010
|
-
traits: traits.filter((t) => typeof t !== "string" && !("ref" in t)).map((t) => ({ ref: String(t.name) }))
|
|
2011
|
-
};
|
|
2012
|
-
});
|
|
2342
|
+
let eventWiring;
|
|
2343
|
+
if (input.eventWiringJson) {
|
|
2344
|
+
eventWiring = JSON.parse(input.eventWiringJson);
|
|
2013
2345
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
orbital.pages = pages.filter((p) => {
|
|
2018
|
-
const pageTraits = p.traits || [];
|
|
2019
|
-
return pageTraits.some((pt) => orbitalTraitNames.includes(pt.ref));
|
|
2020
|
-
});
|
|
2346
|
+
let entityMappings;
|
|
2347
|
+
if (input.entityMappingsJson) {
|
|
2348
|
+
entityMappings = JSON.parse(input.entityMappingsJson);
|
|
2021
2349
|
}
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2350
|
+
const result = composeBehaviors({
|
|
2351
|
+
appName: input.appName || "Application",
|
|
2352
|
+
orbitals,
|
|
2353
|
+
layoutStrategy: input.layoutStrategy ?? "auto",
|
|
2354
|
+
eventWiring,
|
|
2355
|
+
entityMappings
|
|
2356
|
+
});
|
|
2027
2357
|
const schemaPath = path3.join(workDir, "schema.orb");
|
|
2028
|
-
fs3.writeFileSync(schemaPath, JSON.stringify(schema, null, 2));
|
|
2029
|
-
const validation = validateWithOrbitalCLI(schema, workDir);
|
|
2358
|
+
fs3.writeFileSync(schemaPath, JSON.stringify(result.schema, null, 2));
|
|
2359
|
+
const validation = validateWithOrbitalCLI(result.schema, workDir);
|
|
2030
2360
|
return {
|
|
2031
2361
|
success: true,
|
|
2032
|
-
appName: schema.name,
|
|
2362
|
+
appName: result.schema.name,
|
|
2033
2363
|
orbitalCount: orbitals.length,
|
|
2034
|
-
pageCount:
|
|
2364
|
+
pageCount: result.layout.pageCount,
|
|
2035
2365
|
schemaPath,
|
|
2036
2366
|
validation: {
|
|
2037
2367
|
valid: validation.valid ?? false,
|
|
@@ -2041,12 +2371,14 @@ function createComposeTool(workDir) {
|
|
|
2041
2371
|
};
|
|
2042
2372
|
},
|
|
2043
2373
|
{
|
|
2044
|
-
name: "
|
|
2045
|
-
description: "Compose multiple orbitals from .orbitals/ into a final
|
|
2374
|
+
name: "compose_behaviors",
|
|
2375
|
+
description: "Compose multiple orbitals from .orbitals/ into a final application schema using composeBehaviors. Handles layout strategy detection, event wiring, and page generation. Writes schema.orb and validates.",
|
|
2046
2376
|
schema: z.object({
|
|
2047
2377
|
appName: z.string().optional().describe("Application name"),
|
|
2048
2378
|
orbitalFiles: z.string().optional().describe("JSON array of orbital filenames. Defaults to all .json files in .orbitals/"),
|
|
2049
|
-
|
|
2379
|
+
layoutStrategy: z.enum(["sidebar", "tabs", "dashboard", "wizard-flow", "auto"]).optional().describe("Layout strategy. Defaults to auto."),
|
|
2380
|
+
eventWiringJson: z.string().optional().describe("JSON array of event wiring entries: [{ from, event, to, triggers }]"),
|
|
2381
|
+
entityMappingsJson: z.string().optional().describe('JSON object of entity mappings: { "Entity.field": "OtherEntity.field" }')
|
|
2050
2382
|
})
|
|
2051
2383
|
}
|
|
2052
2384
|
);
|