@almadar/agent 1.2.2 → 1.3.1

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/index.js CHANGED
@@ -1,16 +1,15 @@
1
1
  import { AgentDomainCategorySchema, isOrbitalDefinition, getTraitName, isPageReferenceString, isPageReferenceObject, isEntityReference } from '@almadar/core/types';
2
- import { z } from 'zod';
2
+ import { getFullOrbitalPrompt, getRequirementsTraitPrompt, getKeyBehaviorsReference, getSExprQuickRef, getCommonErrorsSection, getArchitectureSection, getMinimalTypeReference } from '@almadar/skills';
3
3
  import { tool } from '@langchain/core/tools';
4
+ import { z } from 'zod';
5
+ import { LLMClient, isStructuredOutputAvailable, getStructuredOutputClient, createRequirementsClient, ANTHROPIC_MODELS, createAnthropicClient, OPENROUTER_MODELS, createOpenRouterClient, KIMI_MODELS, createKimiClient, OPENAI_MODELS, createOpenAIClient, DEEPSEEK_MODELS, createDeepSeekClient } from '@almadar/llm';
4
6
  import { exec, spawn } from 'child_process';
5
7
  import * as path from 'path';
6
8
  import { promisify } from 'util';
7
9
  import * as fs4 from 'fs/promises';
8
- import { isStructuredOutputAvailable, getStructuredOutputClient, LLMClient, createRequirementsClient, ANTHROPIC_MODELS, createAnthropicClient, OPENAI_MODELS, createOpenAIClient, DEEPSEEK_MODELS, createDeepSeekClient } from '@almadar/llm';
9
10
  import * as domain_language_star from '@almadar/core/domain-language';
10
11
  import * as fs3 from 'fs';
11
12
  import crypto, { randomUUID } from 'crypto';
12
- import { getFullOrbitalPrompt, getRequirementsTraitPrompt, getKeyBehaviorsReference, getSExprQuickRef, getCommonErrorsSection, getArchitectureSection, getMinimalTypeReference, generateKflowDesignSkill } from '@almadar/skills';
13
- import { formatRecommendationsForPrompt, buildRecommendationContext, recommendPatterns } from '@almadar/patterns';
14
13
  import { GitHubIntegration } from '@almadar/integrations';
15
14
  import '@langchain/core/messages';
16
15
  import { FilesystemBackend, createDeepAgent } from 'deepagents';
@@ -394,6 +393,855 @@ var init_cache = __esm({
394
393
  init_prompt_assembler();
395
394
  }
396
395
  });
396
+
397
+ // src/orbitals/shared/constants.ts
398
+ var PROVIDER_CONCURRENCY_LIMITS, PROVIDER_BATCH_SIZES, BATCH_MAX_TOKENS;
399
+ var init_constants = __esm({
400
+ "src/orbitals/shared/constants.ts"() {
401
+ PROVIDER_CONCURRENCY_LIMITS = {
402
+ anthropic: 3,
403
+ openai: 5,
404
+ deepseek: 3,
405
+ kimi: 3,
406
+ openrouter: 5
407
+ };
408
+ PROVIDER_BATCH_SIZES = {
409
+ anthropic: 3,
410
+ // Anthropic works well with 3 orbitals per call
411
+ openai: 5,
412
+ // OpenAI can handle more
413
+ deepseek: 3,
414
+ // Conservative for DeepSeek
415
+ kimi: 3,
416
+ // Conservative for Kimi
417
+ openrouter: 5
418
+ // OpenRouter proxies multiple providers
419
+ };
420
+ BATCH_MAX_TOKENS = 12e3;
421
+ }
422
+ });
423
+ function getEntityName2(entity) {
424
+ if (isEntityReference(entity)) {
425
+ return entity.replace(".entity", "");
426
+ }
427
+ return entity.name;
428
+ }
429
+ function getInlineEntity3(entity) {
430
+ if (isEntityReference(entity)) {
431
+ return null;
432
+ }
433
+ return entity;
434
+ }
435
+ function getFieldNames(entity) {
436
+ const inlineEntity = getInlineEntity3(entity);
437
+ if (!inlineEntity?.fields?.length) {
438
+ return "N/A";
439
+ }
440
+ return inlineEntity.fields.map((f) => f.name).join(", ");
441
+ }
442
+ function createLog(level, message, data) {
443
+ return {
444
+ timestamp: Date.now(),
445
+ level,
446
+ message,
447
+ data
448
+ };
449
+ }
450
+ function buildContextSection2(orbital) {
451
+ const parts = [];
452
+ if (orbital.domainContext) {
453
+ const ctx = orbital.domainContext;
454
+ parts.push(`DomainContext: category=${ctx.category || "business"}`);
455
+ if (ctx.vocabulary) {
456
+ parts.push(`Vocabulary: ${JSON.stringify(ctx.vocabulary)}`);
457
+ }
458
+ if (ctx.requestFragment) {
459
+ parts.push(`RequestFragment: "${ctx.requestFragment}"`);
460
+ }
461
+ }
462
+ if (orbital.design) {
463
+ const d = orbital.design;
464
+ if (d.style) parts.push(`Style: ${d.style}`);
465
+ if (d.uxHints) {
466
+ const hints = d.uxHints;
467
+ if (hints.flowPattern) parts.push(`FlowPattern: ${hints.flowPattern}`);
468
+ if (hints.listPattern) parts.push(`ListPattern: ${hints.listPattern}`);
469
+ if (hints.formPattern) parts.push(`FormPattern: ${hints.formPattern}`);
470
+ }
471
+ }
472
+ return parts.length > 0 ? `Context: ${parts.join(", ")}
473
+ ` : "";
474
+ }
475
+ function buildConnectivitySection(orbital) {
476
+ const parts = [];
477
+ if (orbital.emits?.length) {
478
+ parts.push(`Emits: ${orbital.emits.join(", ")}`);
479
+ }
480
+ if (orbital.listens?.length) {
481
+ parts.push(`Listens: ${orbital.listens.map((l) => `${l.event}\u2192${l.triggers}`).join(", ")}`);
482
+ }
483
+ return parts.join("\n");
484
+ }
485
+ function chunkArray(array, size) {
486
+ const chunks = [];
487
+ for (let i = 0; i < array.length; i += size) {
488
+ chunks.push(array.slice(i, i + size));
489
+ }
490
+ return chunks;
491
+ }
492
+ function getErrorMessage(error) {
493
+ if (error instanceof Error) {
494
+ return error.message;
495
+ }
496
+ return String(error);
497
+ }
498
+ var init_utils = __esm({
499
+ "src/orbitals/shared/utils.ts"() {
500
+ }
501
+ });
502
+
503
+ // src/orbitals/shared/index.ts
504
+ var init_shared = __esm({
505
+ "src/orbitals/shared/index.ts"() {
506
+ init_constants();
507
+ init_utils();
508
+ }
509
+ });
510
+ function extractSharedContext(orbitals) {
511
+ const vocabulary = {};
512
+ const styles = /* @__PURE__ */ new Set();
513
+ const patterns = /* @__PURE__ */ new Set();
514
+ const relationships = [];
515
+ for (const orbital of orbitals) {
516
+ if (orbital.domainContext?.vocabulary) {
517
+ Object.assign(vocabulary, orbital.domainContext.vocabulary);
518
+ }
519
+ if (orbital.design?.style) {
520
+ styles.add(orbital.design.style);
521
+ }
522
+ }
523
+ new Map(orbitals.map((o) => [o.name, o]));
524
+ for (const orbital of orbitals) {
525
+ if (orbital.emits) {
526
+ for (const event of orbital.emits) {
527
+ for (const other of orbitals) {
528
+ if (other === orbital) continue;
529
+ if (other.listens) {
530
+ const listener = other.listens.find((l) => l.event === event.event);
531
+ if (listener) {
532
+ relationships.push({
533
+ emitter: orbital.name,
534
+ event: event.event,
535
+ listener: other.name,
536
+ handler: listener.triggers
537
+ });
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+ }
544
+ return {
545
+ domainVocabulary: Object.keys(vocabulary).length > 0 ? vocabulary : void 0,
546
+ designStyle: styles.size === 1 ? Array.from(styles)[0] : void 0,
547
+ commonPatterns: patterns.size > 0 ? Array.from(patterns) : void 0,
548
+ eventRelationships: relationships.length > 0 ? relationships : void 0
549
+ };
550
+ }
551
+ function assembleBatchPrompt(orbitals, options = {}) {
552
+ const {
553
+ baseSystemPrompt = getFullOrbitalPrompt(),
554
+ includeConnectivity = true
555
+ } = options;
556
+ if (orbitals.length === 0) {
557
+ throw new Error("Cannot assemble batch prompt for empty orbitals array");
558
+ }
559
+ const sharedContext = extractSharedContext(orbitals);
560
+ const batchHeader = buildBatchHeader(orbitals, sharedContext);
561
+ const orbitalSections = orbitals.map(
562
+ (orbital, index) => buildOrbitalSection(orbital, index + 1, orbitals.length, includeConnectivity)
563
+ );
564
+ const outputSection = buildOutputSection(orbitals);
565
+ const prompt = `${baseSystemPrompt}
566
+
567
+ ${"=".repeat(60)}
568
+ BATCH GENERATION: ${orbitals.length} ORBITALS
569
+ ${"=".repeat(60)}
570
+
571
+ ${batchHeader}
572
+
573
+ ${orbitalSections.join("\n\n")}
574
+
575
+ ${outputSection}`;
576
+ const fingerprint = generateBatchFingerprint(orbitals);
577
+ return {
578
+ prompt,
579
+ fingerprint,
580
+ usedCachedTemplate: false,
581
+ // Batch doesn't use templates yet
582
+ orbitalCount: orbitals.length,
583
+ orbitalNames: orbitals.map((o) => o.name)
584
+ };
585
+ }
586
+ function buildBatchHeader(orbitals, sharedContext) {
587
+ const lines = [
588
+ `## Batch Overview`,
589
+ `- Total Orbitals: ${orbitals.length}`,
590
+ `- Orbitals: ${orbitals.map((o) => o.name).join(", ")}`
591
+ ];
592
+ if (sharedContext.designStyle) {
593
+ lines.push(`- Design Style: ${sharedContext.designStyle}`);
594
+ }
595
+ if (sharedContext.commonPatterns?.length) {
596
+ lines.push(`- Common Patterns: ${sharedContext.commonPatterns.join(", ")}`);
597
+ }
598
+ if (sharedContext.domainVocabulary && Object.keys(sharedContext.domainVocabulary).length > 0) {
599
+ lines.push(`
600
+ ## Shared Domain Vocabulary`);
601
+ for (const [term, definition] of Object.entries(sharedContext.domainVocabulary)) {
602
+ lines.push(`- ${term}: ${definition}`);
603
+ }
604
+ }
605
+ if (sharedContext.eventRelationships?.length) {
606
+ lines.push(`
607
+ ## Cross-Orbital Event Relationships`);
608
+ for (const rel of sharedContext.eventRelationships) {
609
+ lines.push(`- ${rel.emitter} emits "${rel.event}" \u2192 ${rel.listener} handles with "${rel.handler}"`);
610
+ }
611
+ }
612
+ return lines.join("\n");
613
+ }
614
+ function buildOrbitalSection(orbital, index, total, includeConnectivity) {
615
+ const entityName = getEntityName2(orbital.entity);
616
+ const fieldNames = getFieldNames(orbital.entity);
617
+ const contextSection = buildContextSection2(orbital);
618
+ const connectivitySection = includeConnectivity ? buildConnectivitySection(orbital) : "";
619
+ return `---
620
+ ## ORBITAL ${index}/${total}: ${orbital.name}
621
+
622
+ **Entity**: ${entityName}
623
+ **Persistence**: ${orbital.entity.persistence || "persistent"}
624
+ **Fields**: ${fieldNames}
625
+ **Traits**: ${orbital.traits.map((t) => typeof t === "string" ? t : "ref" in t ? t.ref : t.name).join(", ")}
626
+ ${contextSection}${connectivitySection ? `**Connectivity**:
627
+ ${connectivitySection}
628
+ ` : ""}
629
+ Generate a complete FullOrbitalUnit for ${orbital.name} with:
630
+ - Full field definitions with types and validation
631
+ - Trait state machines with transitions and effects
632
+ - Business rule validation using "guard" S-expression on SAVE transitions
633
+ - Pages with trait references
634
+ - domainContext with category, vocabulary, and requestFragment
635
+ - design with style and uxHints (flowPattern, listPattern, formPattern)
636
+ ${orbital.emits?.length ? "- PRESERVE emits: " + orbital.emits.map((e) => e.event).join(", ") : ""}
637
+ ${orbital.listens?.length ? "- PRESERVE listens: " + orbital.listens.map((l) => l.event).join(", ") : ""}`;
638
+ }
639
+ function buildOutputSection(orbitals) {
640
+ return `---
641
+ ## OUTPUT FORMAT
642
+
643
+ Return a JSON object with this exact structure:
644
+
645
+ \`\`\`json
646
+ {
647
+ "orbitals": [
648
+ ${orbitals.map((o, i) => ` {
649
+ "name": "${o.name}",
650
+ // ... complete FullOrbitalUnit for ${o.name}
651
+ }${i < orbitals.length - 1 ? "," : ""}`).join("\n")}
652
+ ]
653
+ }
654
+ \`\`\`
655
+
656
+ **CRITICAL RULES:**
657
+ 1. Return a SINGLE JSON object with an "orbitals" array
658
+ 2. Each element in the array is a complete FullOrbitalUnit
659
+ 3. Maintain the order: ${orbitals.map((o) => o.name).join(", ")}
660
+ 4. PRESERVE all emits/listens as specified in each orbital section
661
+ 5. Use shared domain vocabulary consistently across all orbitals
662
+ 6. Ensure cross-orbital event wiring is maintained
663
+ `;
664
+ }
665
+ function generateBatchFingerprint(orbitals) {
666
+ const fingerprints = orbitals.map((o) => computeOrbitalFingerprint(o));
667
+ const combined = fingerprints.sort().join("|");
668
+ let hash = 0;
669
+ for (let i = 0; i < combined.length; i++) {
670
+ const char = combined.charCodeAt(i);
671
+ hash = (hash << 5) - hash + char;
672
+ hash = hash & hash;
673
+ }
674
+ return `batch:${orbitals.length}:${Math.abs(hash).toString(16).slice(0, 8)}`;
675
+ }
676
+ function splitIntoBatches(orbitals, options = {}) {
677
+ const {
678
+ maxBatchSize = 3,
679
+ preserveRelationships = true
680
+ } = options;
681
+ if (!preserveRelationships) {
682
+ return chunkArray(orbitals, maxBatchSize);
683
+ }
684
+ const clusters = groupByRelationships(orbitals);
685
+ const batches = [];
686
+ for (const cluster of clusters) {
687
+ if (cluster.length <= maxBatchSize) {
688
+ batches.push(cluster);
689
+ } else {
690
+ batches.push(...chunkArray(cluster, maxBatchSize));
691
+ }
692
+ }
693
+ return batches;
694
+ }
695
+ function groupByRelationships(orbitals) {
696
+ const visited = /* @__PURE__ */ new Set();
697
+ const clusters = [];
698
+ const adjacency = /* @__PURE__ */ new Map();
699
+ for (const orbital of orbitals) {
700
+ if (!adjacency.has(orbital.name)) {
701
+ adjacency.set(orbital.name, /* @__PURE__ */ new Set());
702
+ }
703
+ if (orbital.emits) {
704
+ for (const event of orbital.emits) {
705
+ for (const other of orbitals) {
706
+ if (other === orbital) continue;
707
+ if (other.listens?.some((l) => l.event === event.event)) {
708
+ adjacency.get(orbital.name)?.add(other.name);
709
+ if (!adjacency.has(other.name)) {
710
+ adjacency.set(other.name, /* @__PURE__ */ new Set());
711
+ }
712
+ adjacency.get(other.name)?.add(orbital.name);
713
+ }
714
+ }
715
+ }
716
+ }
717
+ }
718
+ function dfs(orbital, cluster) {
719
+ visited.add(orbital.name);
720
+ cluster.push(orbital);
721
+ const neighbors = adjacency.get(orbital.name) || /* @__PURE__ */ new Set();
722
+ for (const neighborName of neighbors) {
723
+ if (!visited.has(neighborName)) {
724
+ const neighbor = orbitals.find((o) => o.name === neighborName);
725
+ if (neighbor) {
726
+ dfs(neighbor, cluster);
727
+ }
728
+ }
729
+ }
730
+ }
731
+ for (const orbital of orbitals) {
732
+ if (!visited.has(orbital.name)) {
733
+ const cluster = [];
734
+ dfs(orbital, cluster);
735
+ clusters.push(cluster);
736
+ }
737
+ }
738
+ return clusters;
739
+ }
740
+ function estimateBatchTokens(orbitals) {
741
+ const baseTokens = 2e3;
742
+ const perOrbitalTokens = 800;
743
+ return baseTokens + orbitals.length * perOrbitalTokens;
744
+ }
745
+ function willBatchFit(orbitals, maxTokens = 12e3) {
746
+ const estimated = estimateBatchTokens(orbitals);
747
+ return estimated < maxTokens * 0.5;
748
+ }
749
+ var init_prompt_assembler2 = __esm({
750
+ "src/orbitals/batch/prompt-assembler.ts"() {
751
+ init_cache();
752
+ init_shared();
753
+ }
754
+ });
755
+
756
+ // src/orbitals/batch/concurrency.ts
757
+ function createConcurrencyController(maxConcurrency) {
758
+ let activeCount = 0;
759
+ const queue = [];
760
+ return {
761
+ async acquire() {
762
+ if (activeCount < maxConcurrency) {
763
+ activeCount++;
764
+ return Promise.resolve();
765
+ }
766
+ return new Promise((resolve2) => {
767
+ queue.push(() => {
768
+ activeCount++;
769
+ resolve2();
770
+ });
771
+ });
772
+ },
773
+ release() {
774
+ activeCount = Math.max(0, activeCount - 1);
775
+ const next = queue.shift();
776
+ if (next) {
777
+ next();
778
+ }
779
+ },
780
+ get activeCount() {
781
+ return activeCount;
782
+ },
783
+ get waitingCount() {
784
+ return queue.length;
785
+ }
786
+ };
787
+ }
788
+ async function runWithConcurrency(tasks, options) {
789
+ const { concurrency, onProgress } = options;
790
+ const controller = createConcurrencyController(concurrency);
791
+ const results = [];
792
+ let completed = 0;
793
+ await Promise.all(
794
+ tasks.map(async (task, index) => {
795
+ await controller.acquire();
796
+ try {
797
+ results[index] = await task();
798
+ completed++;
799
+ onProgress?.(completed, tasks.length);
800
+ } finally {
801
+ controller.release();
802
+ }
803
+ })
804
+ );
805
+ return results;
806
+ }
807
+ async function asyncMapWithConcurrency2(items, mapper, concurrency) {
808
+ return runWithConcurrency(
809
+ items.map((item, index) => () => mapper(item, index)),
810
+ { concurrency }
811
+ );
812
+ }
813
+ var init_concurrency = __esm({
814
+ "src/orbitals/batch/concurrency.ts"() {
815
+ }
816
+ });
817
+
818
+ // src/orbitals/batch/batch-generator.ts
819
+ async function generateOrbitalsBatch(client, orbitals, options = {}) {
820
+ const startTime = Date.now();
821
+ const aggregateLogs = [];
822
+ const provider = client.getProvider();
823
+ const mode = options.mode || "single-call";
824
+ const maxConcurrency = options.concurrency ?? PROVIDER_CONCURRENCY_LIMITS[provider];
825
+ const maxBatchSize = options.batchSize ?? PROVIDER_BATCH_SIZES[provider];
826
+ console.log(`[BatchGenerator] Starting batch generation: ${orbitals.length} orbitals, mode=${mode}, concurrency=${maxConcurrency}`);
827
+ aggregateLogs.push(createLog("info", `Starting batch generation`, {
828
+ totalOrbitals: orbitals.length,
829
+ mode,
830
+ provider,
831
+ maxConcurrency,
832
+ maxBatchSize
833
+ }));
834
+ const batches = splitIntoBatches(orbitals, {
835
+ maxBatchSize,
836
+ preserveRelationships: options.preserveRelationships ?? true
837
+ });
838
+ console.log(`[BatchGenerator] Split into ${batches.length} batches: [${batches.map((b) => b.length).join(", ")}]`);
839
+ aggregateLogs.push(createLog("info", `Split into ${batches.length} batches`, {
840
+ batchSizes: batches.map((b) => b.length)
841
+ }));
842
+ options.onBatchProgress?.({
843
+ type: "batch_start",
844
+ batchIndex: 0,
845
+ totalBatches: batches.length,
846
+ completedOrbitals: 0,
847
+ totalOrbitals: orbitals.length
848
+ });
849
+ let batchResults;
850
+ if (mode === "parallel-individual") {
851
+ batchResults = await generateParallelIndividual(client, orbitals, {
852
+ ...options,
853
+ concurrency: maxConcurrency,
854
+ onBatchProgress: options.onBatchProgress
855
+ });
856
+ } else if (mode === "single-call") {
857
+ batchResults = await generateSingleCallBatches(client, batches, {
858
+ ...options,
859
+ concurrency: maxConcurrency,
860
+ onBatchProgress: options.onBatchProgress
861
+ });
862
+ } else {
863
+ batchResults = await generateAdaptive(client, orbitals, batches, {
864
+ ...options,
865
+ concurrency: maxConcurrency,
866
+ maxBatchSize,
867
+ onBatchProgress: options.onBatchProgress
868
+ });
869
+ }
870
+ const totalDurationMs = Date.now() - startTime;
871
+ const allResults = batchResults.flatMap((b) => b.results);
872
+ const successful = allResults.filter((r) => r.success).length;
873
+ const failed = allResults.length - successful;
874
+ const totalTokens = batchResults.reduce(
875
+ (sum, b) => sum + (b.usage?.totalTokens ?? 0),
876
+ 0
877
+ );
878
+ console.log(`[BatchGenerator] Complete: ${successful}/${allResults.length} successful, ${totalTokens} tokens, ${totalDurationMs}ms`);
879
+ aggregateLogs.push(createLog("info", `Batch generation completed`, {
880
+ totalDurationMs,
881
+ successful,
882
+ failed,
883
+ totalTokens,
884
+ totalBatches: batches.length
885
+ }));
886
+ options.onBatchProgress?.({
887
+ type: "batch_complete",
888
+ batchIndex: batches.length - 1,
889
+ totalBatches: batches.length,
890
+ completedOrbitals: successful,
891
+ totalOrbitals: orbitals.length
892
+ });
893
+ return {
894
+ results: allResults.map((r) => ({
895
+ orbital: r.orbital,
896
+ fingerprint: "",
897
+ // TODO: compute from orbital
898
+ usedTemplate: false,
899
+ usage: void 0,
900
+ validation: r.success ? { valid: true, errorCount: 0, warningCount: 0 } : { valid: false, errorCount: 1, warningCount: 0 },
901
+ logs: []
902
+ })),
903
+ totalDurationMs,
904
+ aggregateLogs,
905
+ summary: {
906
+ total: allResults.length,
907
+ successful,
908
+ failed,
909
+ totalTokens
910
+ },
911
+ batchResults,
912
+ totalBatches: batches.length
913
+ };
914
+ }
915
+ async function generateSingleCallBatches(client, batches, options) {
916
+ let completedOrbitals = 0;
917
+ const totalOrbitals = batches.reduce((sum, b) => sum + b.length, 0);
918
+ return asyncMapWithConcurrency2(
919
+ batches,
920
+ async (batch, batchIndex) => {
921
+ return generateSingleBatch(client, batch, batchIndex, {
922
+ ...options,
923
+ onProgress: (completedInBatch) => {
924
+ const newCompleted = completedOrbitals + completedInBatch;
925
+ options.onBatchProgress?.({
926
+ type: "orbital_complete",
927
+ batchIndex,
928
+ totalBatches: batches.length,
929
+ completedOrbitals: newCompleted,
930
+ totalOrbitals
931
+ });
932
+ completedOrbitals = newCompleted;
933
+ }
934
+ });
935
+ },
936
+ options.concurrency
937
+ );
938
+ }
939
+ async function generateParallelIndividual(client, orbitals, options) {
940
+ const batches = orbitals.map((o) => [o]);
941
+ return generateSingleCallBatches(client, batches, {
942
+ ...options,
943
+ concurrency: options.concurrency
944
+ });
945
+ }
946
+ async function generateAdaptive(client, orbitals, batches, options) {
947
+ const allFit = batches.every((batch) => willBatchFit(batch, BATCH_MAX_TOKENS));
948
+ if (allFit) {
949
+ console.log(`[BatchGenerator] Adaptive: Using single-call batches (${batches.length} batches)`);
950
+ return generateSingleCallBatches(client, batches, options);
951
+ }
952
+ console.log(`[BatchGenerator] Adaptive: Using parallel-individual (batches too large)`);
953
+ return generateParallelIndividual(client, orbitals, options);
954
+ }
955
+ async function generateSingleBatch(client, orbitals, batchIndex, options) {
956
+ const batchStartTime = Date.now();
957
+ const logs = [];
958
+ console.log(`[BatchGenerator] Starting batch ${batchIndex + 1}: ${orbitals.map((o) => o.name).join(", ")}`);
959
+ logs.push(createLog("info", `Starting batch ${batchIndex + 1}`, {
960
+ orbitalCount: orbitals.length,
961
+ orbitals: orbitals.map((o) => o.name)
962
+ }));
963
+ try {
964
+ const batchPrompt = assembleBatchPrompt(orbitals);
965
+ console.log(`[BatchGenerator] Batch ${batchIndex + 1} prompt assembled: ${batchPrompt.prompt.length} chars`);
966
+ logs.push(createLog("info", `Batch prompt assembled`, {
967
+ promptLength: batchPrompt.prompt.length,
968
+ estimatedTokens: Math.ceil(batchPrompt.prompt.length / 4)
969
+ }));
970
+ const llmResult = await client.callWithMetadata({
971
+ systemPrompt: batchPrompt.prompt,
972
+ userPrompt: "Generate all orbitals in the batch. Return valid JSON matching the output format specified.",
973
+ maxTokens: BATCH_MAX_TOKENS,
974
+ skipSchemaValidation: true
975
+ });
976
+ logs.push(createLog("info", `LLM call completed`, {
977
+ promptTokens: llmResult.usage?.promptTokens,
978
+ completionTokens: llmResult.usage?.completionTokens
979
+ }));
980
+ const parsed = parseBatchResult(llmResult.data, orbitals);
981
+ console.log(`[BatchGenerator] Batch ${batchIndex + 1} parsed: ${parsed.filter((p) => p.success).length}/${parsed.length} successful`);
982
+ for (let i = 0; i < parsed.length; i++) {
983
+ options.onProgress?.(i + 1);
984
+ }
985
+ const durationMs = Date.now() - batchStartTime;
986
+ logs.push(createLog("info", `Batch ${batchIndex + 1} completed`, {
987
+ durationMs,
988
+ successful: parsed.filter((r) => r.success).length
989
+ }));
990
+ return {
991
+ orbitals,
992
+ results: parsed,
993
+ usage: llmResult.usage ?? void 0,
994
+ durationMs,
995
+ logs
996
+ };
997
+ } catch (error) {
998
+ const errorMessage = getErrorMessage(error);
999
+ console.error(`[BatchGenerator] Batch ${batchIndex + 1} FAILED: ${errorMessage}`);
1000
+ logs.push(createLog("error", `Batch ${batchIndex + 1} failed`, {
1001
+ error: errorMessage
1002
+ }));
1003
+ return {
1004
+ orbitals,
1005
+ results: orbitals.map((o) => ({
1006
+ orbital: o,
1007
+ success: false,
1008
+ error: errorMessage
1009
+ })),
1010
+ durationMs: Date.now() - batchStartTime,
1011
+ logs
1012
+ };
1013
+ }
1014
+ }
1015
+ function parseBatchResult(data, expectedOrbitals) {
1016
+ if (!data || typeof data !== "object") {
1017
+ return expectedOrbitals.map((o) => ({
1018
+ orbital: o,
1019
+ success: false,
1020
+ error: "Invalid response: expected object"
1021
+ }));
1022
+ }
1023
+ const obj = data;
1024
+ if (!obj.orbitals || !Array.isArray(obj.orbitals)) {
1025
+ return expectedOrbitals.map((o) => ({
1026
+ orbital: o,
1027
+ success: false,
1028
+ error: "Invalid response: missing orbitals array"
1029
+ }));
1030
+ }
1031
+ const results = obj.orbitals;
1032
+ return expectedOrbitals.map((expected, index) => {
1033
+ const result = results[index];
1034
+ if (!result) {
1035
+ return {
1036
+ orbital: expected,
1037
+ success: false,
1038
+ error: `Missing result for orbital ${index + 1}`
1039
+ };
1040
+ }
1041
+ if (!result.name) {
1042
+ return {
1043
+ orbital: expected,
1044
+ success: false,
1045
+ error: "Generated orbital missing name"
1046
+ };
1047
+ }
1048
+ return {
1049
+ orbital: result,
1050
+ success: true
1051
+ };
1052
+ });
1053
+ }
1054
+ var init_batch_generator = __esm({
1055
+ "src/orbitals/batch/batch-generator.ts"() {
1056
+ init_shared();
1057
+ init_prompt_assembler2();
1058
+ init_concurrency();
1059
+ }
1060
+ });
1061
+
1062
+ // src/orbitals/batch/index.ts
1063
+ var init_batch = __esm({
1064
+ "src/orbitals/batch/index.ts"() {
1065
+ init_batch_generator();
1066
+ init_prompt_assembler2();
1067
+ init_concurrency();
1068
+ }
1069
+ });
1070
+
1071
+ // src/tools/orbital-batch-subagent.ts
1072
+ var orbital_batch_subagent_exports = {};
1073
+ __export(orbital_batch_subagent_exports, {
1074
+ createOrbitalBatchSubagentTool: () => createOrbitalBatchSubagentTool
1075
+ });
1076
+ function createOrbitalBatchSubagentTool(options = {}) {
1077
+ let eventCallback = options.onSubagentEvent;
1078
+ let completeCallback = options.onBatchComplete;
1079
+ const requirements = options.requirements;
1080
+ const workDir = options.workDir;
1081
+ const setEventCallback = (callback) => {
1082
+ eventCallback = callback;
1083
+ };
1084
+ const setBatchCompleteCallback = (callback) => {
1085
+ completeCallback = callback;
1086
+ };
1087
+ const emitEvent = (orbitalName, orbitalIndex, totalOrbitals, type, data) => {
1088
+ if (eventCallback) {
1089
+ eventCallback(orbitalName, orbitalIndex, totalOrbitals, {
1090
+ type,
1091
+ data,
1092
+ timestamp: Date.now()
1093
+ });
1094
+ }
1095
+ };
1096
+ const batchTool = tool(
1097
+ async ({ orbitals, options: batchOptions }) => {
1098
+ if (!orbitals || orbitals.length === 0) {
1099
+ return JSON.stringify({
1100
+ success: false,
1101
+ error: "No orbitals provided for batch generation.",
1102
+ orbitals: []
1103
+ });
1104
+ }
1105
+ console.log(`[OrbitalBatchSubagent] Starting batch generation for ${orbitals.length} orbitals`);
1106
+ try {
1107
+ emitEvent("batch", 0, 1, "message", {
1108
+ content: `Starting batch generation for ${orbitals.length} orbitals`,
1109
+ role: "assistant",
1110
+ isComplete: false
1111
+ });
1112
+ emitEvent("batch", 0, 1, "todo_update", {
1113
+ todos: orbitals.map((o, i) => ({
1114
+ id: `orbital-${i}`,
1115
+ task: `Generate ${o.name}`,
1116
+ status: "pending"
1117
+ }))
1118
+ });
1119
+ const client = new LLMClient({
1120
+ provider: options.provider || "anthropic",
1121
+ model: options.model || "claude-sonnet-4-20250514"
1122
+ });
1123
+ const generationOptions = {
1124
+ mode: batchOptions?.mode || "adaptive",
1125
+ batchSize: batchOptions?.batchSize,
1126
+ concurrency: batchOptions?.maxConcurrency ?? PROVIDER_CONCURRENCY_LIMITS[client.getProvider()],
1127
+ preserveRelationships: batchOptions?.preserveRelationships ?? true,
1128
+ requirements,
1129
+ onBatchProgress: (event) => {
1130
+ if (event.type === "orbital_complete" && event.orbitalName) {
1131
+ emitEvent(event.orbitalName || "batch", event.batchIndex, event.totalBatches, "todo_update", {
1132
+ todos: orbitals.map((o, i) => ({
1133
+ id: `orbital-${i}`,
1134
+ task: `Generate ${o.name}`,
1135
+ status: o.name === event.orbitalName ? "completed" : event.completedOrbitals > i ? "completed" : "pending"
1136
+ }))
1137
+ });
1138
+ }
1139
+ emitEvent(event.orbitalName || "batch", event.batchIndex, event.totalBatches, "generation_log", {
1140
+ level: "info",
1141
+ message: `Progress: ${event.completedOrbitals}/${event.totalOrbitals} orbitals complete`,
1142
+ data: {
1143
+ batchIndex: event.batchIndex,
1144
+ completedOrbitals: event.completedOrbitals,
1145
+ totalOrbitals: event.totalOrbitals
1146
+ }
1147
+ });
1148
+ }
1149
+ };
1150
+ emitEvent("batch", 0, 1, "tool_call", {
1151
+ tool: "generateOrbitalsBatch",
1152
+ args: {
1153
+ orbitalCount: orbitals.length,
1154
+ mode: generationOptions.mode,
1155
+ concurrency: generationOptions.concurrency
1156
+ }
1157
+ });
1158
+ const result = await generateOrbitalsBatch(client, orbitals, generationOptions);
1159
+ emitEvent("batch", result.totalBatches - 1, result.totalBatches, "todo_update", {
1160
+ todos: orbitals.map((o, i) => ({
1161
+ id: `orbital-${i}`,
1162
+ task: `Generate ${o.name}`,
1163
+ status: result.batchResults.some(
1164
+ (b) => b.results.some((r) => r.orbital.name === o.name && r.success)
1165
+ ) ? "completed" : "failed"
1166
+ }))
1167
+ });
1168
+ const generatedOrbitals = result.results.filter((r) => r.orbital).map((r) => r.orbital);
1169
+ const successCount = generatedOrbitals.length;
1170
+ const failedCount = orbitals.length - successCount;
1171
+ emitEvent("batch", result.totalBatches - 1, result.totalBatches, "message", {
1172
+ content: `Batch generation complete: ${successCount}/${orbitals.length} orbitals generated successfully`,
1173
+ role: "assistant",
1174
+ isComplete: true
1175
+ });
1176
+ if (workDir && completeCallback && generatedOrbitals.length > 0) {
1177
+ try {
1178
+ await completeCallback(generatedOrbitals, 0, 1);
1179
+ emitEvent("batch", result.totalBatches - 1, result.totalBatches, "generation_log", {
1180
+ level: "info",
1181
+ message: `Persisted ${generatedOrbitals.length} orbitals`
1182
+ });
1183
+ } catch (persistError) {
1184
+ console.error(`[OrbitalBatchSubagent] Failed to persist orbitals:`, persistError);
1185
+ emitEvent("batch", result.totalBatches - 1, result.totalBatches, "generation_log", {
1186
+ level: "warn",
1187
+ message: "Failed to persist some orbitals",
1188
+ data: { error: String(persistError) }
1189
+ });
1190
+ }
1191
+ }
1192
+ return JSON.stringify({
1193
+ success: successCount === orbitals.length,
1194
+ generated: successCount,
1195
+ failed: failedCount,
1196
+ total: orbitals.length,
1197
+ orbitals: generatedOrbitals,
1198
+ duration: result.totalDurationMs,
1199
+ totalTokens: result.summary.totalTokens,
1200
+ batches: result.totalBatches
1201
+ });
1202
+ } catch (error) {
1203
+ const errorMessage = error instanceof Error ? error.message : String(error);
1204
+ console.error(`[OrbitalBatchSubagent] Batch generation failed:`, errorMessage);
1205
+ emitEvent("batch", 0, 1, "error", {
1206
+ error: errorMessage,
1207
+ code: "BATCH_GENERATION_ERROR"
1208
+ });
1209
+ return JSON.stringify({
1210
+ success: false,
1211
+ error: errorMessage,
1212
+ orbitals: []
1213
+ });
1214
+ }
1215
+ },
1216
+ {
1217
+ name: "generate_orbitals_batch",
1218
+ description: "Generate multiple orbitals in optimized batches. MUCH FASTER than calling generate_orbital multiple times. Use this when generating 3+ orbitals. Supports parallel generation with automatic concurrency control.",
1219
+ schema: OrbitalBatchInputSchema
1220
+ }
1221
+ );
1222
+ return {
1223
+ tool: batchTool,
1224
+ setEventCallback,
1225
+ setBatchCompleteCallback
1226
+ };
1227
+ }
1228
+ var OrbitalBatchInputSchema;
1229
+ var init_orbital_batch_subagent = __esm({
1230
+ "src/tools/orbital-batch-subagent.ts"() {
1231
+ init_shared();
1232
+ init_batch();
1233
+ OrbitalBatchInputSchema = z.object({
1234
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1235
+ orbitals: z.array(z.any()).describe("Array of OrbitalUnits to generate"),
1236
+ options: z.object({
1237
+ mode: z.enum(["single-call", "parallel-individual", "adaptive"]).optional().default("adaptive"),
1238
+ batchSize: z.number().optional(),
1239
+ maxConcurrency: z.number().optional(),
1240
+ preserveRelationships: z.boolean().optional().default(true)
1241
+ }).optional().describe("Batch generation options")
1242
+ });
1243
+ }
1244
+ });
397
1245
  var ExtractedRequirementsSchema = z.object({
398
1246
  /** Entity names to create */
399
1247
  entities: z.array(z.string()).optional().default([]),
@@ -423,7 +1271,7 @@ var GenerateRequestSchema = z.object({
423
1271
  /** Optional: Workspace directory (defaults to temp dir) */
424
1272
  workspace: z.string().optional(),
425
1273
  /** Optional: LLM provider */
426
- provider: z.enum(["anthropic", "openai", "deepseek"]).optional(),
1274
+ provider: z.enum(["anthropic", "openai", "deepseek", "kimi", "openrouter"]).optional(),
427
1275
  /** Optional: Model name */
428
1276
  model: z.string().optional(),
429
1277
  /** Optional: Disable human-in-the-loop interrupts (for eval/testing) */
@@ -1341,6 +2189,77 @@ npx kflow domain:validate input.orb --verbose
1341
2189
 
1342
2190
  // src/tools/finish-task.ts
1343
2191
  var execAsync2 = promisify(exec);
2192
+ var PROP_CORRECTIONS = {
2193
+ onSubmit: "submitEvent",
2194
+ onCancel: "cancelEvent",
2195
+ headerActions: "actions",
2196
+ loading: "isLoading",
2197
+ fieldNames: "fields"
2198
+ };
2199
+ function autoCorrectProps(schema) {
2200
+ let corrections = 0;
2201
+ JSON.stringify(schema);
2202
+ function walkAndFix(obj) {
2203
+ if (Array.isArray(obj)) {
2204
+ return obj.map(walkAndFix);
2205
+ }
2206
+ if (obj && typeof obj === "object") {
2207
+ const result = {};
2208
+ for (const [key, value] of Object.entries(obj)) {
2209
+ if (key in PROP_CORRECTIONS) {
2210
+ result[PROP_CORRECTIONS[key]] = walkAndFix(value);
2211
+ corrections++;
2212
+ } else {
2213
+ result[key] = walkAndFix(value);
2214
+ }
2215
+ }
2216
+ return result;
2217
+ }
2218
+ return obj;
2219
+ }
2220
+ const fixed = walkAndFix(schema);
2221
+ Object.assign(schema, fixed);
2222
+ return corrections;
2223
+ }
2224
+ function checkCompositionQuality(schema) {
2225
+ const warnings = [];
2226
+ const orbitals = schema.orbitals;
2227
+ if (!Array.isArray(orbitals)) return warnings;
2228
+ for (const orbital of orbitals) {
2229
+ const orbObj = orbital;
2230
+ const traits = orbObj.traits;
2231
+ if (!Array.isArray(traits)) continue;
2232
+ for (const trait of traits) {
2233
+ const traitObj = trait;
2234
+ const transitions = traitObj.stateMachine?.transitions;
2235
+ if (!Array.isArray(transitions)) continue;
2236
+ for (const transition of transitions) {
2237
+ const trans = transition;
2238
+ if (trans.event !== "INIT") continue;
2239
+ const effects = trans.effects;
2240
+ if (!Array.isArray(effects)) continue;
2241
+ const mainRenderUIs = effects.filter(
2242
+ (e) => Array.isArray(e) && e[0] === "render-ui" && e[1] === "main"
2243
+ );
2244
+ if (mainRenderUIs.length > 1) {
2245
+ warnings.push(
2246
+ `\u26A0\uFE0F ${orbObj.name}/${traitObj.name} INIT has ${mainRenderUIs.length} flat render-ui calls to main. Should be a single composed stack with children.`
2247
+ );
2248
+ }
2249
+ if (mainRenderUIs.length === 1) {
2250
+ const renderPayload = mainRenderUIs[0];
2251
+ const payload = renderPayload[2];
2252
+ if (payload && payload.type !== "stack" && !payload.children) {
2253
+ warnings.push(
2254
+ `\u26A0\uFE0F ${orbObj.name}/${traitObj.name} INIT renders a single flat ${payload.type} to main. Should be a composed stack with header, metrics, and data sections.`
2255
+ );
2256
+ }
2257
+ }
2258
+ }
2259
+ }
2260
+ }
2261
+ return warnings;
2262
+ }
1344
2263
  async function collectOrbitalsFromDir(workDir) {
1345
2264
  const orbitalsDir = path.join(workDir, ".orbitals");
1346
2265
  try {
@@ -1395,6 +2314,8 @@ function createFinishTaskTool(workDir) {
1395
2314
  let stats = null;
1396
2315
  let validationResult = null;
1397
2316
  let source = null;
2317
+ let propCorrections = 0;
2318
+ let compositionWarnings = [];
1398
2319
  if (workDir) {
1399
2320
  const orbitals = await collectOrbitalsFromDir(workDir);
1400
2321
  if (orbitals.length > 0) {
@@ -1439,6 +2360,10 @@ function createFinishTaskTool(workDir) {
1439
2360
  } catch {
1440
2361
  }
1441
2362
  }
2363
+ if (combinedSchema) {
2364
+ propCorrections = autoCorrectProps(combinedSchema);
2365
+ compositionWarnings = checkCompositionQuality(combinedSchema);
2366
+ }
1442
2367
  if (combinedSchema) {
1443
2368
  const schemaPath = path.join(workDir, "schema.json");
1444
2369
  await fs4.writeFile(schemaPath, JSON.stringify(combinedSchema, null, 2));
@@ -1458,6 +2383,10 @@ function createFinishTaskTool(workDir) {
1458
2383
  errorCount: validationResult.errors?.length || 0,
1459
2384
  warningCount: validationResult.warnings?.length || 0
1460
2385
  } : void 0,
2386
+ designQuality: {
2387
+ propCorrections: propCorrections || 0,
2388
+ compositionWarnings: compositionWarnings || []
2389
+ },
1461
2390
  schemaPath: combinedSchema ? path.join(workDir, "schema.json") : input.schemaPath,
1462
2391
  nextAction: "NONE - Task is complete. Output a brief success message to the user."
1463
2392
  };
@@ -1745,8 +2674,8 @@ function createGenerateOrbitalDomainTool(options = {}) {
1745
2674
  orbitalName: spec.name
1746
2675
  });
1747
2676
  const client = new LLMClient({
1748
- provider: "anthropic",
1749
- model: "claude-sonnet-4-20250514",
2677
+ provider: options.provider ?? "anthropic",
2678
+ model: options.model ?? "claude-sonnet-4-20250514",
1750
2679
  temperature: 0
1751
2680
  });
1752
2681
  const userPrompt = buildDynamicUserPrompt(spec);
@@ -1952,20 +2881,8 @@ function createDomainOrbitalTools(options = {}) {
1952
2881
 
1953
2882
  // src/orbitals/generation/orbital-generator.ts
1954
2883
  init_cache();
1955
- function getEntityName2(entity) {
1956
- if (isEntityReference(entity)) {
1957
- return entity.replace(".entity", "");
1958
- }
1959
- return entity.name;
1960
- }
1961
- function createLog(level, message, data) {
1962
- return {
1963
- timestamp: Date.now(),
1964
- level,
1965
- message,
1966
- data
1967
- };
1968
- }
2884
+ init_shared();
2885
+ init_shared();
1969
2886
  async function generateFullOrbital(client, orbital, options = {}) {
1970
2887
  const {
1971
2888
  maxTokens = 8192,
@@ -2238,8 +3155,8 @@ function createOrbitalSubagentTool(options = {}) {
2238
3155
  ]
2239
3156
  });
2240
3157
  const client = new LLMClient({
2241
- provider: "anthropic",
2242
- model: "claude-sonnet-4-20250514"
3158
+ provider: options.provider ?? "anthropic",
3159
+ model: options.model ?? "claude-sonnet-4-20250514"
2243
3160
  });
2244
3161
  emitEvent(orbitalUnit.name, orbitalIndex, totalOrbitals, "tool_call", {
2245
3162
  tool: "llm_generate",
@@ -2371,6 +3288,9 @@ function createSubagentEventWrapper(writeEvent) {
2371
3288
  writeEvent(sseEvent);
2372
3289
  };
2373
3290
  }
3291
+
3292
+ // src/tools/index.ts
3293
+ init_orbital_batch_subagent();
2374
3294
  function getTraitGenerationPrompt() {
2375
3295
  return `You are a Trait Generator for KFlow orbital schemas.
2376
3296
 
@@ -3016,273 +3936,6 @@ function createSchemaChunkingTools(workDir) {
3016
3936
  applyChunk: createApplyChunkTool(workDir)
3017
3937
  };
3018
3938
  }
3019
- var designCache = /* @__PURE__ */ new Map();
3020
- var CACHE_TTL_MS2 = 24 * 60 * 60 * 1e3;
3021
- var CACHE_VERSION2 = 1;
3022
- function generateFingerprint2(input) {
3023
- const normalized = JSON.stringify({
3024
- version: CACHE_VERSION2,
3025
- ...input
3026
- });
3027
- return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
3028
- }
3029
- function getCached2(fingerprint) {
3030
- const entry = designCache.get(fingerprint);
3031
- if (!entry) return null;
3032
- if (Date.now() - entry.timestamp > CACHE_TTL_MS2) {
3033
- designCache.delete(fingerprint);
3034
- return null;
3035
- }
3036
- return entry;
3037
- }
3038
- var STATIC_DESIGN_PROMPT = null;
3039
- function getDesignSystemPrompt() {
3040
- if (!STATIC_DESIGN_PROMPT) {
3041
- const skill = generateKflowDesignSkill();
3042
- STATIC_DESIGN_PROMPT = skill.content;
3043
- }
3044
- return STATIC_DESIGN_PROMPT;
3045
- }
3046
- function getPatternRecommendations(input) {
3047
- const recContext = buildRecommendationContext({
3048
- state: input.from,
3049
- event: input.event,
3050
- slot: input.slot,
3051
- domainCategory: input.domainCategory,
3052
- entityFields: input.entityFields
3053
- });
3054
- return recommendPatterns(recContext, 8);
3055
- }
3056
- function buildDesignUserPrompt(input) {
3057
- const fieldList = input.entityFields.map((f) => {
3058
- let desc = ` - ${f.name}: ${f.type}`;
3059
- if (f.values) desc += ` (values: ${f.values.join(", ")})`;
3060
- return desc;
3061
- }).join("\n");
3062
- const hints = [];
3063
- if (input.designStyle) hints.push(`Style: ${input.designStyle}`);
3064
- if (input.flowPattern) hints.push(`Flow: ${input.flowPattern}`);
3065
- if (input.listPattern) hints.push(`List: ${input.listPattern}`);
3066
- if (input.formPattern) hints.push(`Form: ${input.formPattern}`);
3067
- if (input.detailPattern) hints.push(`Detail: ${input.detailPattern}`);
3068
- const vocab = input.vocabulary ? Object.entries(input.vocabulary).map(([k, v]) => ` ${k} \u2192 "${v}"`).join("\n") : "";
3069
- return `Design render-ui effects for this transition:
3070
-
3071
- ## Transition
3072
- - **From**: ${input.from}
3073
- - **To**: ${input.to}
3074
- - **Event**: ${input.event}
3075
- - **Slot**: ${input.slot}
3076
-
3077
- ## Entity: ${input.entityName}
3078
- ${fieldList}
3079
-
3080
- ## Domain
3081
- - **Category**: ${input.domainCategory || "business"}
3082
- ${vocab ? `- **Vocabulary**:
3083
- ${vocab}` : ""}
3084
- ${hints.length > 0 ? `- **Design Hints**: ${hints.join(", ")}` : ""}
3085
-
3086
- ${input.recommendationsSection || ""}
3087
-
3088
- ${input.existingEffects ? `## Existing Effects (enhance these)
3089
- \`\`\`json
3090
- ${JSON.stringify(input.existingEffects, null, 2)}
3091
- \`\`\`` : ""}
3092
-
3093
- Return ONLY the JSON array of render-ui effect tuples.`;
3094
- }
3095
- var DesignTransitionSchema = z.object({
3096
- from: z.string().describe('Source state name (e.g., "Browsing")'),
3097
- to: z.string().describe('Target state name (e.g., "Browsing" for self-loop, "Creating" for transition)'),
3098
- event: z.string().describe('Event name (e.g., "INIT", "CREATE", "VIEW", "EDIT", "DELETE", "SAVE", "CANCEL")'),
3099
- slot: z.string().describe('UI slot to render into: "main", "modal", "drawer", "sidebar", "overlay"'),
3100
- entityName: z.string().describe('Entity name (e.g., "Task", "Order")'),
3101
- entityFields: z.array(z.object({
3102
- name: z.string(),
3103
- type: z.string(),
3104
- values: z.array(z.string()).optional()
3105
- })).describe("Entity fields with types and optional enum values"),
3106
- domainCategory: AgentDomainCategorySchema.optional().describe("Domain category for pattern selection"),
3107
- vocabulary: z.record(z.string(), z.string()).optional().describe('Domain vocabulary mapping (e.g., { "create": "Place Order", "item": "Order" })'),
3108
- designStyle: z.enum(["minimal", "modern", "playful", "data-driven", "immersive"]).optional().describe("Visual style hint"),
3109
- flowPattern: z.enum(["hub-spoke", "master-detail", "crud-cycle", "linear", "role-based"]).optional().describe("Application flow pattern"),
3110
- listPattern: z.enum(["entity-table", "entity-cards", "entity-list"]).optional().describe("Preferred list pattern"),
3111
- formPattern: z.enum(["modal", "drawer", "page"]).optional().describe("Preferred form pattern"),
3112
- detailPattern: z.enum(["drawer", "page", "split"]).optional().describe("Preferred detail view pattern"),
3113
- existingEffects: z.array(z.any()).optional().describe("Existing render-ui effects to enhance (for refinement passes)")
3114
- });
3115
- function createDesignTransitionTool(options = {}) {
3116
- let eventCallback = options.onEvent;
3117
- const setEventCallback = (callback) => {
3118
- eventCallback = callback;
3119
- };
3120
- const emitEvent = (transitionId, type, data) => {
3121
- if (eventCallback) {
3122
- eventCallback(transitionId, {
3123
- type,
3124
- data,
3125
- timestamp: Date.now()
3126
- });
3127
- }
3128
- };
3129
- const designTransitionTool = tool(
3130
- async (input) => {
3131
- const transitionId = `${input.entityName}:${input.from}->${input.to}:${input.event}`;
3132
- const fingerprint = generateFingerprint2({
3133
- from: input.from,
3134
- to: input.to,
3135
- event: input.event,
3136
- slot: input.slot,
3137
- entityName: input.entityName,
3138
- entityFields: input.entityFields,
3139
- domainCategory: input.domainCategory,
3140
- designStyle: input.designStyle,
3141
- flowPattern: input.flowPattern,
3142
- listPattern: input.listPattern
3143
- });
3144
- try {
3145
- emitEvent(transitionId, "message", {
3146
- content: `Designing UI for ${transitionId}`,
3147
- role: "assistant",
3148
- isComplete: false
3149
- });
3150
- const cached = getCached2(fingerprint);
3151
- if (cached) {
3152
- emitEvent(transitionId, "generation_log", {
3153
- level: "info",
3154
- message: `Design cache HIT for ${transitionId}`,
3155
- data: { fingerprint }
3156
- });
3157
- return JSON.stringify({
3158
- success: true,
3159
- transitionId,
3160
- effects: cached.effects,
3161
- cached: true,
3162
- usage: cached.usage
3163
- });
3164
- }
3165
- const recommendations = getPatternRecommendations(input);
3166
- const recommendationsSection = formatRecommendationsForPrompt(recommendations);
3167
- const systemPrompt = getDesignSystemPrompt();
3168
- const userPrompt = buildDesignUserPrompt({
3169
- ...input,
3170
- recommendationsSection
3171
- });
3172
- emitEvent(transitionId, "tool_call", {
3173
- tool: "llm_design_transition",
3174
- args: {
3175
- transition: transitionId,
3176
- slot: input.slot,
3177
- domain: input.domainCategory
3178
- }
3179
- });
3180
- const client = new LLMClient({
3181
- provider: "anthropic",
3182
- model: "claude-sonnet-4-20250514",
3183
- temperature: 0.1
3184
- // Slight creativity for design
3185
- });
3186
- const response = await client.callWithCache({
3187
- systemPrompt: "",
3188
- systemBlocks: [{
3189
- type: "text",
3190
- text: systemPrompt,
3191
- cache_control: { type: "ephemeral" }
3192
- }],
3193
- userPrompt,
3194
- maxTokens: 4096,
3195
- rawText: true
3196
- });
3197
- const rawText = (response.raw || String(response.data) || "").trim();
3198
- let effects;
3199
- try {
3200
- const jsonText = rawText.replace(/^```(?:json)?\n?/m, "").replace(/\n?```$/m, "").trim();
3201
- effects = JSON.parse(jsonText);
3202
- if (!Array.isArray(effects)) {
3203
- effects = [effects];
3204
- }
3205
- } catch {
3206
- return JSON.stringify({
3207
- success: false,
3208
- transitionId,
3209
- error: "Failed to parse design output as JSON",
3210
- rawOutput: rawText
3211
- });
3212
- }
3213
- const usage = {
3214
- inputTokens: response.usage?.promptTokens || 0,
3215
- outputTokens: response.usage?.completionTokens || 0,
3216
- totalTokens: response.usage?.totalTokens || 0
3217
- };
3218
- designCache.set(fingerprint, {
3219
- effects,
3220
- timestamp: Date.now(),
3221
- usage
3222
- });
3223
- emitEvent(transitionId, "tool_result", {
3224
- tool: "llm_design_transition",
3225
- result: { fingerprint, effectCount: effects.length, usage },
3226
- success: true
3227
- });
3228
- emitEvent(transitionId, "message", {
3229
- content: `Designed ${effects.length} effect(s) for ${transitionId} (${usage.totalTokens} tokens)`,
3230
- role: "assistant",
3231
- isComplete: true
3232
- });
3233
- return JSON.stringify({
3234
- success: true,
3235
- transitionId,
3236
- effects,
3237
- cached: false,
3238
- usage,
3239
- recommendedPatterns: recommendations.map((r) => r.pattern)
3240
- });
3241
- } catch (error) {
3242
- const errorMessage = error instanceof Error ? error.message : String(error);
3243
- emitEvent(transitionId, "error", {
3244
- error: errorMessage,
3245
- code: "DESIGN_TRANSITION_ERROR"
3246
- });
3247
- return JSON.stringify({
3248
- success: false,
3249
- transitionId,
3250
- error: errorMessage
3251
- });
3252
- }
3253
- },
3254
- {
3255
- name: "design_transition",
3256
- description: `Design rich render-ui effects for a single orbital transition.
3257
-
3258
- Takes the transition context (from/to state, event, entity, domain) and produces
3259
- polished render-ui effects using the full pattern catalog.
3260
-
3261
- USE THIS TOOL WHEN:
3262
- - Generating INIT transitions (always compose header + stats + content)
3263
- - Generating CREATE/EDIT transitions (form with proper fields)
3264
- - Generating VIEW transitions (detail with tabs for related entities)
3265
- - Enhancing existing render-ui effects that are too basic
3266
-
3267
- The tool uses a specialized design skill with pattern catalog, layout composition,
3268
- and domain-aware pattern selection to produce rich UI.
3269
-
3270
- RETURNS: { success, effects: [["render-ui", slot, config], ...], transitionId, usage }
3271
-
3272
- The effects array contains ONLY render-ui tuples. Non-UI effects (persist, emit, set)
3273
- are NOT included \u2014 you must preserve those from the original transition.
3274
-
3275
- INTEGRATION: After calling this tool, use extract_chunk to get the orbital,
3276
- replace the render-ui effects in the target transition (keep persist/emit/set effects),
3277
- then apply_chunk to merge back into schema.json.`,
3278
- schema: DesignTransitionSchema
3279
- }
3280
- );
3281
- return {
3282
- tool: designTransitionTool,
3283
- setEventCallback
3284
- };
3285
- }
3286
3939
  function createGitHubTools(config) {
3287
3940
  const { token, owner = "", repo = "", workDir } = config;
3288
3941
  const integrationConfig = {
@@ -3652,9 +4305,7 @@ function createAgentTools(workDir) {
3652
4305
  // Domain tools (now use internal functions)
3653
4306
  domainOrbitalTools: createDomainOrbitalTools({ workDir }),
3654
4307
  // Chunking tools
3655
- schemaChunking: createSchemaChunkingTools(workDir),
3656
- // Design tool
3657
- designTransition: createDesignTransitionTool()
4308
+ schemaChunking: createSchemaChunkingTools(workDir)
3658
4309
  };
3659
4310
  }
3660
4311
  function getSchemaReference() {
@@ -4867,6 +5518,21 @@ function createLLMClient(provider, model, verbose) {
4867
5518
  model: model || OPENAI_MODELS.GPT4O,
4868
5519
  temperature
4869
5520
  });
5521
+ case "kimi":
5522
+ if (verbose)
5523
+ console.log(`[SkillAgent] Using Kimi: ${model || KIMI_MODELS.K2_5}`);
5524
+ return createKimiClient({
5525
+ model: model || KIMI_MODELS.K2_5,
5526
+ temperature: 0.6
5527
+ // Kimi with thinking disabled requires 0.6
5528
+ });
5529
+ case "openrouter":
5530
+ if (verbose)
5531
+ console.log(`[SkillAgent] Using OpenRouter: ${model || OPENROUTER_MODELS.QWEN_2_5_72B}`);
5532
+ return createOpenRouterClient({
5533
+ model: model || OPENROUTER_MODELS.QWEN_2_5_72B,
5534
+ temperature: 0.3
5535
+ });
4870
5536
  case "anthropic":
4871
5537
  default:
4872
5538
  if (verbose)
@@ -4891,6 +5557,8 @@ async function createSkillAgent(options) {
4891
5557
  threadId: providedThreadId,
4892
5558
  provider = "anthropic",
4893
5559
  model,
5560
+ subagentProvider = "anthropic",
5561
+ subagentModel = "claude-sonnet-4-20250514",
4894
5562
  verbose = false,
4895
5563
  skillLoader,
4896
5564
  skillRefLoader
@@ -4987,20 +5655,24 @@ ${skillContents}`;
4987
5655
  const finishTaskTool = createFinishTaskTool(workDir);
4988
5656
  const validateSchemaTool = createValidateSchemaTool(workDir);
4989
5657
  const ORBITAL_SKILLS = ["kflow-orbitals", "kflow-orbital-games", "kflow-orbital-fixing"];
5658
+ const ORBITAL_BATCH_SKILLS = ["kflow-orbitals-batch"];
4990
5659
  const LEAN_SKILLS = ["kflow-lean-orbitals", "kflow-lean-fixing"];
4991
- const DESIGN_SKILLS = ["kflow-design", "kflow-lean-design"];
4992
5660
  const isOrbitalSkill = primarySkill.name === "kflow-orbitals";
5661
+ const isOrbitalBatchSkill = ORBITAL_BATCH_SKILLS.includes(primarySkill.name);
4993
5662
  const isLeanSkill = LEAN_SKILLS.includes(primarySkill.name);
4994
- const isDesignSkill = DESIGN_SKILLS.includes(primarySkill.name);
4995
- const needsChunkingTools = ORBITAL_SKILLS.includes(primarySkill.name) || isDesignSkill;
5663
+ const needsChunkingTools = ORBITAL_SKILLS.includes(primarySkill.name);
4996
5664
  let orbitalTool;
4997
5665
  let setOrbitalEventCallback;
4998
5666
  let setOrbitalCompleteCallback;
5667
+ let orbitalBatchTool;
5668
+ let setBatchEventCallback;
4999
5669
  let domainOrbitalTools;
5000
5670
  const chunkingTools = needsChunkingTools ? createSchemaChunkingTools(workDir) : null;
5001
5671
  if (isOrbitalSkill) {
5002
5672
  const orbitalResult = createOrbitalSubagentTool({
5003
- requirements: options.requirements
5673
+ requirements: options.requirements,
5674
+ provider: subagentProvider,
5675
+ model: subagentModel
5004
5676
  });
5005
5677
  orbitalTool = orbitalResult.tool;
5006
5678
  setOrbitalEventCallback = orbitalResult.setEventCallback;
@@ -5012,16 +5684,32 @@ ${skillContents}`;
5012
5684
  console.log(`[SkillAgent] Orbital tools enabled for kflow-orbitals skill`);
5013
5685
  }
5014
5686
  }
5015
- if (isLeanSkill) {
5016
- domainOrbitalTools = createDomainOrbitalTools({ workDir });
5687
+ if (isOrbitalBatchSkill) {
5688
+ const { createOrbitalBatchSubagentTool: createOrbitalBatchSubagentTool2 } = await Promise.resolve().then(() => (init_orbital_batch_subagent(), orbital_batch_subagent_exports));
5689
+ const batchResult = createOrbitalBatchSubagentTool2({
5690
+ requirements: options.requirements,
5691
+ provider: subagentProvider,
5692
+ model: subagentModel,
5693
+ workDir
5694
+ });
5695
+ orbitalBatchTool = batchResult.tool;
5696
+ setBatchEventCallback = batchResult.setEventCallback;
5697
+ if (options.onSubagentEvent) {
5698
+ setBatchEventCallback(options.onSubagentEvent);
5699
+ }
5017
5700
  if (verbose) {
5018
- console.log(`[SkillAgent] Domain orbital tools enabled for ${primarySkill.name} skill`);
5701
+ console.log(`[SkillAgent] Batch orbital tools enabled for kflow-orbitals-batch skill`);
5019
5702
  }
5020
5703
  }
5021
- const needsDesignTool = isDesignSkill || ORBITAL_SKILLS.includes(primarySkill.name);
5022
- const designTool = needsDesignTool ? createDesignTransitionTool() : null;
5023
- if (designTool && verbose) {
5024
- console.log(`[SkillAgent] Design transition tool enabled for ${primarySkill.name} skill`);
5704
+ if (isLeanSkill) {
5705
+ domainOrbitalTools = createDomainOrbitalTools({
5706
+ workDir,
5707
+ provider: subagentProvider,
5708
+ model: subagentModel
5709
+ });
5710
+ if (verbose) {
5711
+ console.log(`[SkillAgent] Domain orbital tools enabled for ${primarySkill.name} skill (provider: ${subagentProvider})`);
5712
+ }
5025
5713
  }
5026
5714
  const githubTools = options.githubConfig ? createGitHubToolsArray({
5027
5715
  token: options.githubConfig.token,
@@ -5035,8 +5723,9 @@ ${skillContents}`;
5035
5723
  const tools = [
5036
5724
  executeTool,
5037
5725
  finishTaskTool,
5038
- ...isOrbitalSkill ? [] : [validateSchemaTool],
5726
+ ...isOrbitalSkill || isOrbitalBatchSkill ? [] : [validateSchemaTool],
5039
5727
  ...orbitalTool ? [orbitalTool] : [],
5728
+ ...orbitalBatchTool ? [orbitalBatchTool] : [],
5040
5729
  ...domainOrbitalTools ? [
5041
5730
  domainOrbitalTools.generateOrbitalDomain,
5042
5731
  domainOrbitalTools.constructCombinedDomain
@@ -5046,7 +5735,6 @@ ${skillContents}`;
5046
5735
  chunkingTools.extractChunk,
5047
5736
  chunkingTools.applyChunk
5048
5737
  ] : [],
5049
- ...designTool ? [designTool.tool] : [],
5050
5738
  ...githubTools || []
5051
5739
  ];
5052
5740
  const checkpointer = sessions.getCheckpointer(threadId);