@almadar/agent 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import { exec, spawn, execSync } from 'child_process';
5
5
  import * as path from 'path';
6
6
  import path__default from 'path';
7
7
  import { promisify } from 'util';
8
- import * as fs4 from 'fs/promises';
8
+ import * as fs5 from 'fs/promises';
9
9
  import { isStructuredOutputAvailable, getStructuredOutputClient, LLMClient } from '@almadar/llm';
10
10
  import * as domain_language_star from '@almadar/core/domain-language';
11
11
  import * as fs3 from 'fs';
@@ -585,12 +585,12 @@ ${output}`);
585
585
  description: `Execute a shell command in the workspace directory.
586
586
 
587
587
  IMPORTANT: You are restricted to your workspace directory. Use RELATIVE paths only:
588
- - ./schema.json (NOT /schema.json or absolute paths)
588
+ - ./schema.orb (NOT /schema.orb or absolute paths)
589
589
  - ./app (NOT /app or absolute paths)
590
590
 
591
591
  Examples:
592
- - orbital validate ./schema.json --json
593
- - npx kflow compile ./schema.json -o ./app --verify
592
+ - orbital validate ./schema.orb --json
593
+ - npx kflow compile ./schema.orb -o ./app --verify
594
594
  - cd ./app && npm install
595
595
 
596
596
  Commands using absolute paths outside your workspace will be blocked.`,
@@ -604,7 +604,7 @@ Commands using absolute paths outside your workspace will be blocked.`,
604
604
  var execAsync = promisify(exec);
605
605
  var MAX_VALIDATION_ATTEMPTS = 5;
606
606
  function createValidateSchemaTool(workDir) {
607
- const schemaPath = path.join(workDir, "schema.json");
607
+ const schemaPath = path.join(workDir, "schema.orb");
608
608
  let validationAttempts = 0;
609
609
  return tool(
610
610
  async () => {
@@ -620,7 +620,7 @@ function createValidateSchemaTool(workDir) {
620
620
  }
621
621
  try {
622
622
  try {
623
- await fs4.access(schemaPath);
623
+ await fs5.access(schemaPath);
624
624
  } catch {
625
625
  return JSON.stringify({
626
626
  success: false,
@@ -629,7 +629,7 @@ function createValidateSchemaTool(workDir) {
629
629
  errors: [{
630
630
  code: "FILE_NOT_FOUND",
631
631
  path: "",
632
- message: "File not found: schema.json. Create it first with write_file."
632
+ message: "File not found: schema.orb. Create it first with write_file."
633
633
  }]
634
634
  });
635
635
  }
@@ -690,7 +690,7 @@ function createValidateSchemaTool(workDir) {
690
690
  },
691
691
  {
692
692
  name: "validate_schema",
693
- description: `Validate the schema.json file in the workspace.
693
+ description: `Validate the schema.orb file in the workspace.
694
694
 
695
695
  Runs validation using \`npx @almadar/cli validate --json\`.
696
696
 
@@ -1285,14 +1285,14 @@ function checkCompositionQuality(schema) {
1285
1285
  async function collectOrbitalsFromDir(workDir) {
1286
1286
  const orbitalsDir = path.join(workDir, ".orbitals");
1287
1287
  try {
1288
- const dirStat = await fs4.stat(orbitalsDir);
1288
+ const dirStat = await fs5.stat(orbitalsDir);
1289
1289
  if (!dirStat.isDirectory()) {
1290
1290
  return [];
1291
1291
  }
1292
1292
  } catch {
1293
1293
  return [];
1294
1294
  }
1295
- const files = await fs4.readdir(orbitalsDir);
1295
+ const files = await fs5.readdir(orbitalsDir);
1296
1296
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
1297
1297
  if (jsonFiles.length === 0) {
1298
1298
  return [];
@@ -1300,7 +1300,7 @@ async function collectOrbitalsFromDir(workDir) {
1300
1300
  const orbitals = [];
1301
1301
  for (const file of jsonFiles) {
1302
1302
  try {
1303
- const content = await fs4.readFile(path.join(orbitalsDir, file), "utf-8");
1303
+ const content = await fs5.readFile(path.join(orbitalsDir, file), "utf-8");
1304
1304
  orbitals.push(JSON.parse(content));
1305
1305
  } catch {
1306
1306
  }
@@ -1354,7 +1354,7 @@ function createFinishTaskTool(workDir) {
1354
1354
  } else {
1355
1355
  const domainPath = path.join(workDir, "domain.txt");
1356
1356
  try {
1357
- const domainText = await fs4.readFile(domainPath, "utf-8");
1357
+ const domainText = await fs5.readFile(domainPath, "utf-8");
1358
1358
  if (domainText.trim()) {
1359
1359
  source = "domain";
1360
1360
  const appName = input.summary?.match(/Generated \d+ orbitals?:?\s*(.+)/)?.[1] || "Application";
@@ -1387,8 +1387,8 @@ function createFinishTaskTool(workDir) {
1387
1387
  compositionWarnings = checkCompositionQuality(combinedSchema);
1388
1388
  }
1389
1389
  if (combinedSchema) {
1390
- const schemaPath = path.join(workDir, "schema.json");
1391
- await fs4.writeFile(schemaPath, JSON.stringify(combinedSchema, null, 2));
1390
+ const schemaPath = path.join(workDir, "schema.orb");
1391
+ await fs5.writeFile(schemaPath, JSON.stringify(combinedSchema, null, 2));
1392
1392
  validationResult = await validateSchemaWithCLI(schemaPath);
1393
1393
  }
1394
1394
  }
@@ -1409,25 +1409,16 @@ function createFinishTaskTool(workDir) {
1409
1409
  propCorrections: propCorrections || 0,
1410
1410
  compositionWarnings: compositionWarnings || []
1411
1411
  },
1412
- schemaPath: combinedSchema ? path.join(workDir, "schema.json") : input.schemaPath,
1412
+ schemaPath: combinedSchema ? path.join(workDir, "schema.orb") : input.schemaPath,
1413
1413
  nextAction: "NONE - Task is complete. Output a brief success message to the user."
1414
1414
  };
1415
1415
  },
1416
1416
  {
1417
1417
  name: "finish_task",
1418
- description: `Signal that the orbital generation workflow is COMPLETE.
1419
-
1420
- \u26A0\uFE0F IMPORTANT: After calling this tool, STOP. Do not make any more tool calls.
1421
-
1422
- WHAT IT DOES:
1423
- 1. Automatically collects orbitals from .orbitals/ directory
1424
- 2. Combines them into a single OrbitalSchema
1425
- 3. Validates the combined schema
1426
- 4. Writes schema.json to workspace
1427
- 5. Returns success with stats`,
1418
+ description: `Complete workflow: collect orbitals from .orbitals/, combine into schema, validate, write schema.orb. STOP after calling.`,
1428
1419
  schema: z.object({
1429
- summary: z.string().describe("Brief summary of what was accomplished"),
1430
- schemaPath: z.string().optional().describe("Path to the combined schema file if already written")
1420
+ summary: z.string().describe("What was accomplished"),
1421
+ schemaPath: z.string().optional().describe("Schema file path if already written")
1431
1422
  })
1432
1423
  }
1433
1424
  );
@@ -1441,7 +1432,7 @@ function generateFingerprint(spec) {
1441
1432
  entity: spec.entity,
1442
1433
  pages: spec.pages,
1443
1434
  traits: spec.traits.sort(),
1444
- patterns: spec.patterns.sort(),
1435
+ patterns: spec.patterns?.sort() || [],
1445
1436
  emits: spec.emits?.sort() || [],
1446
1437
  listens: spec.listens?.map((l) => `${l.event}:${l.triggers}`).sort() || []
1447
1438
  });
@@ -1578,7 +1569,7 @@ ${relations}` : ""}
1578
1569
  ${pagesInfo}
1579
1570
 
1580
1571
  **Behaviors**: ${spec.traits.join(", ")}
1581
- **UI Patterns**: ${spec.patterns.join(", ")}
1572
+ **UI Patterns**: ${spec.patterns?.join(", ") || "auto-detect from traits"}
1582
1573
  ${emitsInfo}
1583
1574
  ${listensInfo}
1584
1575
 
@@ -1610,7 +1601,7 @@ var GenerateOrbitalDomainSchema = z.object({
1610
1601
  isInitial: z.boolean().optional()
1611
1602
  })),
1612
1603
  traits: z.array(z.string()),
1613
- patterns: z.array(z.string()),
1604
+ patterns: z.array(z.string()).optional(),
1614
1605
  domainContext: z.object({
1615
1606
  request: z.string(),
1616
1607
  requestFragment: z.string().optional(),
@@ -1756,7 +1747,7 @@ function createGenerateOrbitalDomainTool(options = {}) {
1756
1747
  cached: false,
1757
1748
  domainWritten: true,
1758
1749
  usage,
1759
- domainText
1750
+ domainLength: domainText.length
1760
1751
  });
1761
1752
  } catch (error) {
1762
1753
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2131,6 +2122,88 @@ ${sections.join("\n\n")}
2131
2122
  `;
2132
2123
  }
2133
2124
 
2125
+ // src/evals/utils/compact-orbital.ts
2126
+ function compactField(f) {
2127
+ let desc = `${f.name}:${f.type}`;
2128
+ if (f.required) desc += "*";
2129
+ if (f.values) desc += `(${f.values.length})`;
2130
+ if (f.relation) desc += `\u2192${f.relation.entity}`;
2131
+ return desc;
2132
+ }
2133
+ function getStateName(state) {
2134
+ return typeof state === "string" ? state : state.name;
2135
+ }
2136
+ function getTraitRef(ref) {
2137
+ return typeof ref === "string" ? ref : ref.ref;
2138
+ }
2139
+ function compactOrbital(orbital) {
2140
+ const lines = [];
2141
+ lines.push(`orbital: ${orbital.name}`);
2142
+ if (orbital.entity) {
2143
+ const e = orbital.entity;
2144
+ const fieldSummary = e.fields?.map(compactField).join(", ") || "none";
2145
+ const collection = e.collection ? ` (${e.collection})` : "";
2146
+ lines.push(` entity: ${e.name}${collection} [${fieldSummary}] ${e.fields?.length || 0} fields`);
2147
+ }
2148
+ if (orbital.traits) {
2149
+ for (const trait of orbital.traits) {
2150
+ const sm = trait.stateMachine;
2151
+ if (sm) {
2152
+ const stateNames = sm.states?.map(getStateName) || [];
2153
+ const stateList = stateNames.join("\u2192");
2154
+ const stateCount = stateNames.length;
2155
+ const eventCount = sm.events?.length || 0;
2156
+ const transCount = sm.transitions?.length || 0;
2157
+ lines.push(` trait: ${trait.name} [${stateList}] ${stateCount} states, ${eventCount} events, ${transCount} transitions`);
2158
+ } else {
2159
+ lines.push(` trait: ${trait.name}`);
2160
+ }
2161
+ }
2162
+ }
2163
+ if (orbital.pages) {
2164
+ for (const page of orbital.pages) {
2165
+ const traitRefs = page.traits?.map(getTraitRef).join(", ") || "none";
2166
+ lines.push(` page: ${page.path} \u2192 ${traitRefs}`);
2167
+ }
2168
+ }
2169
+ if (orbital.emits?.length) {
2170
+ lines.push(` emits: ${orbital.emits.join(", ")}`);
2171
+ }
2172
+ if (orbital.listens?.length) {
2173
+ lines.push(` listens: ${orbital.listens.map((l) => `${l.event} \u2192 ${l.triggers}`).join(", ")}`);
2174
+ }
2175
+ return lines.join("\n");
2176
+ }
2177
+ function compactSchema(schema) {
2178
+ const lines = [];
2179
+ if (schema.name) {
2180
+ lines.push(`schema: ${schema.name} v${schema.version || "1.0.0"}`);
2181
+ }
2182
+ const orbitals = schema.orbitals || [];
2183
+ lines.push(`orbitals: ${orbitals.length}`);
2184
+ for (const orbital of orbitals) {
2185
+ lines.push(compactOrbital(orbital));
2186
+ }
2187
+ return lines.join("\n");
2188
+ }
2189
+ function compactOrbitalResult(result) {
2190
+ if (!result.orbital) {
2191
+ return JSON.stringify({ success: false, error: "Generation failed" });
2192
+ }
2193
+ const orbital = result.orbital;
2194
+ const summary = compactOrbital(orbital);
2195
+ const validationLine = result.validation ? result.validation.valid ? " validation: valid" : ` validation: INVALID (${result.validation.errors?.map((e) => e.message).join("; ") || "unknown errors"})` : "";
2196
+ const usageLine = result.usage ? ` tokens: ${result.usage.totalTokens || 0}` : "";
2197
+ const compactLines = [
2198
+ `success: true`,
2199
+ `fingerprint: ${result.fingerprint || "none"}`,
2200
+ summary,
2201
+ validationLine,
2202
+ usageLine
2203
+ ].filter(Boolean);
2204
+ return compactLines.join("\n");
2205
+ }
2206
+
2134
2207
  // src/tools/orbital-subagent.ts
2135
2208
  function getEntityName3(entity) {
2136
2209
  if (isEntityReference(entity)) {
@@ -2152,8 +2225,8 @@ var OrbitalInputSchema = z.object({
2152
2225
  })
2153
2226
  )
2154
2227
  }),
2155
- traits: z.array(z.string()),
2156
- patterns: z.array(z.string()),
2228
+ traits: z.array(z.string()).default([]),
2229
+ patterns: z.array(z.string()).optional(),
2157
2230
  pages: z.array(
2158
2231
  z.object({
2159
2232
  name: z.string(),
@@ -2340,7 +2413,16 @@ function createOrbitalSubagentTool(options = {}) {
2340
2413
  });
2341
2414
  }
2342
2415
  }
2343
- return JSON.stringify({
2416
+ if (options.workDir && finalOrbital) {
2417
+ const orbitalsDir = path.join(options.workDir, ".orbitals");
2418
+ fs3.mkdirSync(orbitalsDir, { recursive: true });
2419
+ const safeFileName = orbitalUnit.name.replace(/[^a-zA-Z0-9_-]/g, "_");
2420
+ fs3.writeFileSync(
2421
+ path.join(orbitalsDir, `${safeFileName}.json`),
2422
+ JSON.stringify(finalOrbital, null, 2)
2423
+ );
2424
+ }
2425
+ return compactOrbitalResult({
2344
2426
  success: true,
2345
2427
  orbital: finalOrbital,
2346
2428
  fingerprint: result.fingerprint,
@@ -2362,15 +2444,19 @@ function createOrbitalSubagentTool(options = {}) {
2362
2444
  },
2363
2445
  {
2364
2446
  name: "generate_orbital",
2365
- description: `Generate a full orbital schema from an OrbitalUnit.
2447
+ description: `Generate ONE orbital schema from a single OrbitalUnit.
2366
2448
 
2367
2449
  Spawns a subagent to expand a lightweight OrbitalUnit into a complete
2368
2450
  FullOrbitalUnit with all sections, layouts, and configurations.
2369
2451
 
2452
+ IMPORTANT: This tool generates exactly ONE orbital per call. Each orbital
2453
+ must have exactly ONE entity. For multi-entity apps, call this tool
2454
+ SEPARATELY for each orbital (e.g., 4 entities = 4 calls).
2455
+
2370
2456
  USAGE:
2371
- 1. Decompose user request into OrbitalUnits
2372
- 2. For each orbital, call generate_orbital with embedded context
2373
- 3. Collect results and merge into final schema`,
2457
+ 1. Decompose user request into OrbitalUnits (one per entity)
2458
+ 2. Call generate_orbital ONCE PER ORBITAL \u2014 do NOT batch multiple orbitals
2459
+ 3. After all orbitals are generated, call finish_task`,
2374
2460
  schema: OrbitalInputSchema
2375
2461
  }
2376
2462
  );
@@ -3399,27 +3485,27 @@ var CombineSchemaInputSchema = z.object({
3399
3485
  validate: z.boolean().optional().default(true)
3400
3486
  });
3401
3487
  async function autoCollectOrbitals(workDir) {
3402
- const fs7 = await import('fs');
3403
- const path8 = await import('path');
3404
- const orbitalsDir = path8.join(workDir, ".orbitals");
3405
- if (!fs7.existsSync(orbitalsDir)) {
3406
- const files2 = fs7.readdirSync(workDir).filter(
3488
+ const fs9 = await import('fs');
3489
+ const path10 = await import('path');
3490
+ const orbitalsDir = path10.join(workDir, ".orbitals");
3491
+ if (!fs9.existsSync(orbitalsDir)) {
3492
+ const files2 = fs9.readdirSync(workDir).filter(
3407
3493
  (f) => f.endsWith(".json") && (f.startsWith("orbital-") || f.includes("orbital"))
3408
3494
  );
3409
3495
  if (files2.length === 0) {
3410
3496
  throw new Error("No orbitals found. Generate orbitals first using generate_orbital, or provide them directly.");
3411
3497
  }
3412
3498
  return files2.map((f) => {
3413
- const content = fs7.readFileSync(path8.join(workDir, f), "utf-8");
3499
+ const content = fs9.readFileSync(path10.join(workDir, f), "utf-8");
3414
3500
  return JSON.parse(content);
3415
3501
  });
3416
3502
  }
3417
- const files = fs7.readdirSync(orbitalsDir).filter((f) => f.endsWith(".json"));
3503
+ const files = fs9.readdirSync(orbitalsDir).filter((f) => f.endsWith(".json"));
3418
3504
  if (files.length === 0) {
3419
3505
  throw new Error("No orbitals found in .orbitals/ directory.");
3420
3506
  }
3421
3507
  return files.map((f) => {
3422
- const content = fs7.readFileSync(path8.join(orbitalsDir, f), "utf-8");
3508
+ const content = fs9.readFileSync(path10.join(orbitalsDir, f), "utf-8");
3423
3509
  return JSON.parse(content);
3424
3510
  });
3425
3511
  }
@@ -3521,7 +3607,7 @@ var CHUNKS_DIR = ".chunks";
3521
3607
  async function ensureChunksDir(workspaceDir) {
3522
3608
  const chunksPath = path.join(workspaceDir, CHUNKS_DIR);
3523
3609
  try {
3524
- await fs4.mkdir(chunksPath, { recursive: true });
3610
+ await fs5.mkdir(chunksPath, { recursive: true });
3525
3611
  } catch {
3526
3612
  }
3527
3613
  return chunksPath;
@@ -3545,7 +3631,7 @@ function createQuerySchemaStructureTool(workDir) {
3545
3631
  async ({ file }) => {
3546
3632
  const filePath = path.isAbsolute(file) ? file : path.join(workDir, file);
3547
3633
  try {
3548
- const content = await fs4.readFile(filePath, "utf-8");
3634
+ const content = await fs5.readFile(filePath, "utf-8");
3549
3635
  const schema = JSON.parse(content);
3550
3636
  const stats = fs3.statSync(filePath);
3551
3637
  const structure = {
@@ -3574,7 +3660,7 @@ function createQuerySchemaStructureTool(workDir) {
3574
3660
 
3575
3661
  USE THIS FIRST before extracting chunks to understand what orbitals and traits exist.`,
3576
3662
  schema: z.object({
3577
- file: z.string().describe('Path to schema file (e.g., "schema.json")')
3663
+ file: z.string().describe('Path to schema file (e.g., "schema.orb")')
3578
3664
  })
3579
3665
  }
3580
3666
  );
@@ -3584,7 +3670,7 @@ function createExtractChunkTool(workDir) {
3584
3670
  async ({ file, type, name, parentOrbital, includeTraits = true }) => {
3585
3671
  const filePath = path.isAbsolute(file) ? file : path.join(workDir, file);
3586
3672
  try {
3587
- const content = await fs4.readFile(filePath, "utf-8");
3673
+ const content = await fs5.readFile(filePath, "utf-8");
3588
3674
  const schema = JSON.parse(content);
3589
3675
  const chunkId = randomUUID().slice(0, 8);
3590
3676
  const chunksDir = await ensureChunksDir(workDir);
@@ -3635,7 +3721,7 @@ function createExtractChunkTool(workDir) {
3635
3721
  chunkData.inlineTrait = orbital.traits[inlineTraitIndex];
3636
3722
  }
3637
3723
  const chunkContent = JSON.stringify(chunkData, null, 2);
3638
- await fs4.writeFile(chunkFile, chunkContent);
3724
+ await fs5.writeFile(chunkFile, chunkContent);
3639
3725
  const relativeChunkFile = path.relative(workDir, chunkFile);
3640
3726
  return JSON.stringify({
3641
3727
  success: true,
@@ -3673,14 +3759,14 @@ function createApplyChunkTool(workDir) {
3673
3759
  const chunkFilePath = path.join(workDir, CHUNKS_DIR, chunkFileName);
3674
3760
  try {
3675
3761
  try {
3676
- await fs4.access(chunkFilePath);
3762
+ await fs5.access(chunkFilePath);
3677
3763
  } catch {
3678
3764
  return JSON.stringify({ error: `Chunk file not found: ${chunkFileName}.` });
3679
3765
  }
3680
- const chunkContent = await fs4.readFile(chunkFilePath, "utf-8");
3766
+ const chunkContent = await fs5.readFile(chunkFilePath, "utf-8");
3681
3767
  const chunkData = JSON.parse(chunkContent);
3682
3768
  const { meta } = chunkData;
3683
- const schemaContent = await fs4.readFile(meta.sourceFile, "utf-8");
3769
+ const schemaContent = await fs5.readFile(meta.sourceFile, "utf-8");
3684
3770
  const schema = JSON.parse(schemaContent);
3685
3771
  if (meta.type === "orbital" && chunkData.orbital) {
3686
3772
  schema.orbitals[meta.orbitalIndex] = chunkData.orbital;
@@ -3711,8 +3797,8 @@ function createApplyChunkTool(workDir) {
3711
3797
  }
3712
3798
  }
3713
3799
  const output = JSON.stringify(schema, null, 2);
3714
- await fs4.writeFile(meta.sourceFile, output);
3715
- await fs4.unlink(chunkFilePath);
3800
+ await fs5.writeFile(meta.sourceFile, output);
3801
+ await fs5.unlink(chunkFilePath);
3716
3802
  return JSON.stringify({
3717
3803
  success: true,
3718
3804
  message: `Changes merged into ${path.basename(meta.sourceFile)}`,
@@ -4075,12 +4161,70 @@ function quickComplexityCheck(prompt) {
4075
4161
  estimatedOrbitals: estimatedCounts[estimate.estimatedLevel]
4076
4162
  };
4077
4163
  }
4164
+ function categorizeError(error) {
4165
+ const message = error.message?.toLowerCase() || "";
4166
+ const code = error.code?.toLowerCase() || "";
4167
+ if (message.includes("binding") || code.includes("binding")) {
4168
+ return "binding";
4169
+ }
4170
+ if (message.includes("state") || code.includes("state")) {
4171
+ return "state-machine";
4172
+ }
4173
+ if (message.includes("required") || message.includes("missing")) {
4174
+ return "schema";
4175
+ }
4176
+ return "other";
4177
+ }
4178
+ function getErrorSpecificGuidance(errors) {
4179
+ const types = new Set(errors.map(categorizeError));
4180
+ const guidance = [];
4181
+ if (types.has("binding")) {
4182
+ guidance.push(`
4183
+ ## BINDING ERROR FIXES
4184
+
4185
+ Invalid bindings detected. Fix these patterns:
4186
+
4187
+ \u274C INVALID:
4188
+ - "@count" - missing entity reference
4189
+ - "@count:status=active" - wrong syntax
4190
+ - "@entity" - missing field
4191
+
4192
+ \u2705 VALID:
4193
+ - "@entity.count" - entity field reference
4194
+ - "@entity.status" - enum field reference
4195
+ - "@payload.data" - payload reference
4196
+ - "@state.current" - state reference
4197
+
4198
+ Replace invalid bindings with proper entity.field references.`);
4199
+ }
4200
+ if (types.has("state-machine")) {
4201
+ guidance.push(`
4202
+ ## STATE MACHINE FIXES
4203
+
4204
+ Check for:
4205
+ - Unreachable states (no incoming transitions)
4206
+ - Missing transitions for events
4207
+ - Invalid state names
4208
+
4209
+ Ensure every state (except initial) has at least one incoming transition.`);
4210
+ }
4211
+ if (types.has("schema")) {
4212
+ guidance.push(`
4213
+ ## SCHEMA FIXES
4214
+
4215
+ Check for:
4216
+ - Required fields (name, type for entity fields)
4217
+ - Missing entity definitions
4218
+ - Invalid type values`);
4219
+ }
4220
+ return guidance.join("\n");
4221
+ }
4078
4222
  async function createFixPlan(schema, errors, config) {
4079
4223
  const planStart = Date.now();
4080
4224
  const criticalErrors = errors.filter(
4081
- (e) => e.code?.includes("ENTITY") || e.code?.includes("REQUIRED") || e.code?.includes("JSON")
4225
+ (e) => e.severity === "error" || e.code?.includes("ENTITY") || e.code?.includes("REQUIRED")
4082
4226
  );
4083
- const warningErrors = errors.filter((e) => !criticalErrors.includes(e));
4227
+ const warningErrors = errors.filter((e) => e.severity === "warning");
4084
4228
  const orbitalErrors = /* @__PURE__ */ new Map();
4085
4229
  errors.forEach((error, index) => {
4086
4230
  let pathStr;
@@ -4106,6 +4250,8 @@ async function createFixPlan(schema, errors, config) {
4106
4250
  const orbital = schema.orbitals[orbitalIndex];
4107
4251
  if (orbital) {
4108
4252
  affectedOrbitals.push(orbital.name);
4253
+ const stepErrors = errorIndices.map((i) => errors[i]);
4254
+ const errorType = categorizeError(stepErrors[0]);
4109
4255
  const midPoint = Math.ceil(errorIndices.length / 2);
4110
4256
  const batches = [
4111
4257
  errorIndices.slice(0, midPoint),
@@ -4118,7 +4264,8 @@ async function createFixPlan(schema, errors, config) {
4118
4264
  orbitalIndex,
4119
4265
  errorIndices: batch,
4120
4266
  provider: batchIndex === 0 ? "deepseek" : "qwen",
4121
- dependencies: []
4267
+ dependencies: [],
4268
+ errorType
4122
4269
  });
4123
4270
  });
4124
4271
  }
@@ -4143,13 +4290,14 @@ async function createFixPlan(schema, errors, config) {
4143
4290
  console.log(`[Fixing] Plan created in ${Date.now() - planStart}ms`);
4144
4291
  console.log(`[Fixing] Strategy: ${parallelizationStrategy}`);
4145
4292
  console.log(`[Fixing] Steps: ${executionOrder.length}`);
4293
+ console.log(`[Fixing] Error types: ${[...new Set(executionOrder.map((s) => s.errorType))].join(", ")}`);
4146
4294
  }
4147
4295
  return plan;
4148
4296
  }
4149
4297
  async function executeFixStep(schema, step, errors, config) {
4150
4298
  const orbital = schema.orbitals[step.orbitalIndex];
4151
4299
  if (!orbital) {
4152
- return { success: false, updatedOrbital: null };
4300
+ return { success: false, updatedOrbital: null, fixed: false };
4153
4301
  }
4154
4302
  const client = step.provider === "deepseek" ? new LLMClient({
4155
4303
  provider: "deepseek",
@@ -4159,58 +4307,30 @@ async function executeFixStep(schema, step, errors, config) {
4159
4307
  model: "qwen/qwen3.5-397b-a17b"
4160
4308
  });
4161
4309
  const stepErrors = step.errorIndices.map((i) => errors[i]);
4162
- const skillContent = config.skillContent || "";
4163
- const hasBindingErrors = stepErrors.some(
4164
- (e) => e.message?.toLowerCase().includes("binding") || e.code?.toLowerCase().includes("binding")
4165
- );
4166
- const bindingGuidance = hasBindingErrors ? `
4167
- ## BINDING ERROR FIXES
4168
-
4169
- Common binding mistakes and fixes:
4170
-
4171
- \u274C INVALID:
4172
- - "@count" \u2192 missing entity reference
4173
- - "@status" \u2192 missing entity reference
4174
- - "@count:status=active" \u2192 wrong syntax
4175
-
4176
- \u2705 VALID (from skill):
4177
- - "@entity.count" - entity field reference
4178
- - "@entity.status" - enum field reference
4179
- - "@payload.data" - event payload data
4180
- - "@state" - current state name
4181
- - "@now" - current timestamp
4182
-
4183
- Replace bare field names like "@count" with proper entity references like "@entity.count".
4184
- ` : "";
4185
- const systemPrompt = `${skillContent}
4186
-
4187
- ${bindingGuidance}
4188
-
4189
- ## FIXING INSTRUCTIONS
4190
-
4191
- You are fixing validation errors in an OrbitalSchema orbital.
4192
-
4193
- Rules:
4194
- 1. Preserve ALL valid content - only fix the reported errors
4195
- 2. Return the COMPLETE orbital with entity, traits, and pages
4196
- 3. Ensure entity has: name, collection, and fields array
4197
- 4. Use valid binding formats: @entity.field, @payload.field, @state
4310
+ const errorGuidance = getErrorSpecificGuidance(stepErrors);
4311
+ const skillContext = config.skillContent || "";
4312
+ const systemPrompt = `You are an expert at fixing OrbitalSchema validation errors.
4313
+ ${skillContext ? "\n" + skillContext.slice(0, 2e3) + "\n" : ""}
4314
+ ${errorGuidance}
4315
+
4316
+ CRITICAL RULES:
4317
+ 1. Preserve ALL valid content - only fix the errors
4318
+ 2. Return the COMPLETE orbital with all fields (entity, traits, pages)
4319
+ 3. Ensure the entity field is present with proper name, collection, and fields
4320
+ 4. Use valid binding formats: @entity.field, @payload.field, @state.field
4198
4321
 
4199
4322
  Return ONLY the fixed orbital as valid JSON.`;
4200
- const userPrompt = `Fix these validation errors in orbital "${orbital.name}":
4323
+ const userPrompt = `Fix these errors in orbital "${orbital.name}":
4201
4324
 
4202
- ERRORS:
4203
- ${stepErrors.map((e) => {
4204
- const path8 = Array.isArray(e.path) ? e.path.join(".") : e.path;
4205
- return `- Path: ${path8}
4325
+ ERRORS TO FIX:
4326
+ ${stepErrors.map((e) => `- Path: ${Array.isArray(e.path) ? e.path.join(".") : e.path}
4206
4327
  Code: ${e.code}
4207
- Message: ${e.message}`;
4208
- }).join("\n")}
4328
+ Message: ${e.message}`).join("\n")}
4209
4329
 
4210
4330
  CURRENT ORBITAL (JSON):
4211
4331
  ${JSON.stringify(orbital, null, 2)}
4212
4332
 
4213
- Return the COMPLETE fixed orbital as JSON with all fields (entity, traits, pages).`;
4333
+ Return the COMPLETE fixed orbital as JSON. Include all fields (entity, traits, pages).`;
4214
4334
  try {
4215
4335
  const result = await client.call({
4216
4336
  systemPrompt,
@@ -4218,14 +4338,27 @@ Return the COMPLETE fixed orbital as JSON with all fields (entity, traits, pages
4218
4338
  maxTokens: 8192,
4219
4339
  skipSchemaValidation: true
4220
4340
  });
4341
+ const fixedOrbital = result;
4342
+ const hasEntity = fixedOrbital.entity && typeof fixedOrbital.entity === "object";
4343
+ const hasName = typeof fixedOrbital.name === "string";
4344
+ const hasTraits = Array.isArray(fixedOrbital.traits);
4345
+ const isValid = Boolean(hasEntity && hasName && hasTraits);
4346
+ if (!isValid && config.verbose) {
4347
+ console.log(`[Fixing] Fix validation failed: entity=${hasEntity}, name=${hasName}, traits=${hasTraits}`);
4348
+ }
4221
4349
  return {
4222
- success: true,
4223
- updatedOrbital: result
4350
+ success: isValid,
4351
+ updatedOrbital: result,
4352
+ fixed: isValid
4224
4353
  };
4225
4354
  } catch (error) {
4355
+ if (config.verbose) {
4356
+ console.log(`[Fixing] Fix execution failed: ${error instanceof Error ? error.message : "Unknown"}`);
4357
+ }
4226
4358
  return {
4227
4359
  success: false,
4228
- updatedOrbital: null
4360
+ updatedOrbital: null,
4361
+ fixed: false
4229
4362
  };
4230
4363
  }
4231
4364
  }
@@ -4243,22 +4376,22 @@ async function executeFixPlan(schema, plan, errors, config) {
4243
4376
  Promise.all(qwenSteps.map((step) => executeFixStep(schema, step, errors, config)))
4244
4377
  ]);
4245
4378
  deepseekSteps.forEach((step, i) => {
4246
- if (deepseekResults[i].success) {
4379
+ if (deepseekResults[i].success && deepseekResults[i].fixed) {
4247
4380
  updatedSchema.orbitals[step.orbitalIndex] = deepseekResults[i].updatedOrbital;
4248
4381
  }
4249
4382
  });
4250
4383
  qwenSteps.forEach((step, i) => {
4251
- if (qwenResults[i].success) {
4384
+ if (qwenResults[i].success && qwenResults[i].fixed) {
4252
4385
  updatedSchema.orbitals[step.orbitalIndex] = qwenResults[i].updatedOrbital;
4253
4386
  }
4254
4387
  });
4255
4388
  } else {
4256
4389
  for (const step of plan.executionOrder) {
4257
4390
  if (config.verbose) {
4258
- console.log(`[Fixing] Executing: ${step.description}`);
4391
+ console.log(`[Fixing] Executing: ${step.description} (${step.errorType})`);
4259
4392
  }
4260
4393
  const result = await executeFixStep(schema, step, errors, config);
4261
- if (result.success) {
4394
+ if (result.success && result.fixed) {
4262
4395
  updatedSchema.orbitals[step.orbitalIndex] = result.updatedOrbital;
4263
4396
  }
4264
4397
  }
@@ -4303,10 +4436,10 @@ function validateSchema(schema) {
4303
4436
  }
4304
4437
  }
4305
4438
  }
4306
- async function orchestrateFixing(schema, config = {}) {
4439
+ async function orchestrateFixingV2(schema, config = {}) {
4307
4440
  const totalStart = Date.now();
4308
4441
  if (config.verbose) {
4309
- console.log("[Fixing] Starting Plan-Then-Execute fixing workflow");
4442
+ console.log("[Fixing V2] Starting enhanced Plan-Then-Execute fixing workflow");
4310
4443
  }
4311
4444
  const initialValidation = validateSchema(schema);
4312
4445
  if (initialValidation.valid) {
@@ -4329,6 +4462,12 @@ async function orchestrateFixing(schema, config = {}) {
4329
4462
  }
4330
4463
  };
4331
4464
  }
4465
+ if (config.verbose) {
4466
+ console.log(`[Fixing V2] Found ${initialValidation.errors.length} errors to fix`);
4467
+ initialValidation.errors.forEach((e) => {
4468
+ console.log(` - ${e.code}: ${e.message}`);
4469
+ });
4470
+ }
4332
4471
  const planStart = Date.now();
4333
4472
  const plan = await createFixPlan(schema, initialValidation.errors, config);
4334
4473
  const planMs = Date.now() - planStart;
@@ -4336,17 +4475,21 @@ async function orchestrateFixing(schema, config = {}) {
4336
4475
  let fixedSchema = await executeFixPlan(schema, plan, initialValidation.errors, config);
4337
4476
  const executeMs = Date.now() - executeStart;
4338
4477
  const verifyStart = Date.now();
4339
- const finalValidation = validateSchema(fixedSchema);
4478
+ let finalValidation = validateSchema(fixedSchema);
4340
4479
  const verifyMs = Date.now() - verifyStart;
4341
4480
  let retries = 0;
4342
4481
  const maxRetries = config.maxRetries || 2;
4343
4482
  while (!finalValidation.valid && retries < maxRetries) {
4344
4483
  if (config.verbose) {
4345
- console.log(`[Fixing] Retrying (${retries + 1}/${maxRetries})...`);
4484
+ console.log(`[Fixing V2] Retrying (${retries + 1}/${maxRetries})...`);
4485
+ console.log(`[Fixing V2] Remaining errors: ${finalValidation.errors.length}`);
4346
4486
  }
4347
4487
  const retryPlan = await createFixPlan(fixedSchema, finalValidation.errors, config);
4348
4488
  fixedSchema = await executeFixPlan(fixedSchema, retryPlan, finalValidation.errors, config);
4349
4489
  const retryValidation = validateSchema(fixedSchema);
4490
+ if (config.verbose) {
4491
+ console.log(`[Fixing V2] After retry: ${retryValidation.errors.length} errors remaining`);
4492
+ }
4350
4493
  if (retryValidation.valid || retryValidation.errors.length < finalValidation.errors.length) {
4351
4494
  finalValidation.errors = retryValidation.errors;
4352
4495
  finalValidation.valid = retryValidation.valid;
@@ -4355,11 +4498,11 @@ async function orchestrateFixing(schema, config = {}) {
4355
4498
  }
4356
4499
  const totalMs = Date.now() - totalStart;
4357
4500
  if (config.verbose) {
4358
- console.log("[Fixing] === Summary ===");
4359
- console.log(`[Fixing] Plan: ${planMs}ms, Execute: ${executeMs}ms, Verify: ${verifyMs}ms`);
4360
- console.log(`[Fixing] Total: ${totalMs}ms`);
4361
- console.log(`[Fixing] Errors: ${initialValidation.errors.length} \u2192 ${finalValidation.errors.length}`);
4362
- console.log(`[Fixing] Success: ${finalValidation.valid ? "YES" : "PARTIAL"}`);
4501
+ console.log("[Fixing V2] === Summary ===");
4502
+ console.log(`[Fixing V2] Plan: ${planMs}ms, Execute: ${executeMs}ms, Verify: ${verifyMs}ms`);
4503
+ console.log(`[Fixing V2] Total: ${totalMs}ms`);
4504
+ console.log(`[Fixing V2] Errors: ${initialValidation.errors.length} \u2192 ${finalValidation.errors.length}`);
4505
+ console.log(`[Fixing V2] Success: ${finalValidation.valid ? "YES" : "PARTIAL"}`);
4363
4506
  }
4364
4507
  return {
4365
4508
  success: finalValidation.valid,
@@ -4406,9 +4549,25 @@ function createOrchestratedGenerationTool(options) {
4406
4549
  version: "1.0.0",
4407
4550
  orbitals: result.orbitals
4408
4551
  };
4552
+ if (options.workDir) {
4553
+ const schemaPath = path.join(options.workDir, "schema.orb");
4554
+ fs3.writeFileSync(schemaPath, JSON.stringify(schema, null, 2));
4555
+ const orbitalsDir = path.join(options.workDir, ".orbitals");
4556
+ fs3.mkdirSync(orbitalsDir, { recursive: true });
4557
+ for (const orbital of result.orbitals) {
4558
+ const name = ("name" in orbital ? String(orbital.name) : "unknown").replace(/[^a-zA-Z0-9_-]/g, "_");
4559
+ fs3.writeFileSync(
4560
+ path.join(orbitalsDir, `${name}.json`),
4561
+ JSON.stringify(orbital, null, 2)
4562
+ );
4563
+ }
4564
+ }
4565
+ const summary = compactSchema(schema);
4409
4566
  return {
4410
4567
  success: true,
4411
- schema,
4568
+ summary,
4569
+ orbitalCount: result.orbitals.length,
4570
+ schemaPath: options.workDir ? path.join(options.workDir, "schema.orb") : void 0,
4412
4571
  complexity: result.complexity,
4413
4572
  provider: result.provider,
4414
4573
  timing: result.timing
@@ -4426,16 +4585,9 @@ function createOrchestratedGenerationTool(options) {
4426
4585
  },
4427
4586
  {
4428
4587
  name: "generate_schema_orchestrated",
4429
- description: `Generate an OrbitalSchema using complexity-based provider routing.
4430
-
4431
- This tool automatically selects the best generation strategy:
4432
- - Simple (1 orbital) \u2192 Fast single-provider generation
4433
- - Medium (2-3 orbitals) \u2192 Efficient single-provider generation
4434
- - Complex (4+ orbitals) \u2192 Parallel multi-provider generation
4435
-
4436
- The tool handles decomposition, provider selection, and result combination automatically.`,
4588
+ description: `Generate an OrbitalSchema with automatic complexity-based provider routing.`,
4437
4589
  schema: z.object({
4438
- prompt: z.string().describe("User prompt describing the application to generate")
4590
+ prompt: z.string().describe("App description prompt")
4439
4591
  })
4440
4592
  }
4441
4593
  );
@@ -4530,7 +4682,7 @@ function createOrchestratedFixingTool(options) {
4530
4682
  console.log(`[OrchestratedFixing] Found ${initialValidation.errors.length} errors to fix`);
4531
4683
  }
4532
4684
  try {
4533
- const result = await orchestrateFixing(typedSchema, {
4685
+ const result = await orchestrateFixingV2(typedSchema, {
4534
4686
  maxRetries,
4535
4687
  verbose,
4536
4688
  skillContent