@adhdev/mesh-shared 1.0.30-rc.1 → 1.0.30-rc.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.
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export * from './daemon-normalize';
20
20
  export * from './git-summarize';
21
21
  export * from './magi';
22
22
  export * from './brain-routing';
23
+ export * from './slot-proposal';
23
24
  export * from './interpolation';
24
25
  export * from './mesh-tool-names';
25
26
  export * from './mesh-status-probe';
package/dist/index.js CHANGED
@@ -22,11 +22,15 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CANONICAL_MESH_TOOL_COUNT: () => CANONICAL_MESH_TOOL_COUNT,
24
24
  CANONICAL_MESH_TOOL_NAMES: () => CANONICAL_MESH_TOOL_NAMES,
25
+ CLI_SLOT_RECIPES: () => CLI_SLOT_RECIPES,
25
26
  DEFAULT_DIFFICULTY_BRAINS: () => DEFAULT_DIFFICULTY_BRAINS,
26
27
  MAGI_RAW_ANSWER_CAP: () => MAGI_RAW_ANSWER_CAP,
27
28
  MESH_TASK_DIFFICULTIES: () => MESH_TASK_DIFFICULTIES,
28
29
  STATUS_PROBE_ARG_KEY: () => STATUS_PROBE_ARG_KEY,
30
+ UNKNOWN_CLI_SLOT_RECIPE: () => UNKNOWN_CLI_SLOT_RECIPE,
29
31
  argsCarryStatusProbeMarker: () => argsCarryStatusProbeMarker,
32
+ buildMagiPanelProposal: () => buildMagiPanelProposal,
33
+ buildSlotProposal: () => buildSlotProposal,
30
34
  canonicalDaemonId: () => canonicalDaemonId,
31
35
  daemonIdsEquivalent: () => daemonIdsEquivalent,
32
36
  deriveSlotsFromLegacy: () => deriveSlotsFromLegacy,
@@ -511,6 +515,162 @@ function deriveSlotsFromLegacy(input) {
511
515
  });
512
516
  }
513
517
 
518
+ // src/slot-proposal.ts
519
+ var CLI_SLOT_RECIPES = Object.freeze({
520
+ "claude-cli": [
521
+ {
522
+ model: "sonnet",
523
+ thinkingLevel: "high",
524
+ difficulty: ["medium", "easy"],
525
+ maxParallel: 5,
526
+ rationale: "Primary workhorse \u2014 widest parallelism for routine work."
527
+ },
528
+ {
529
+ model: "opus",
530
+ thinkingLevel: "high",
531
+ difficulty: ["difficult"],
532
+ maxParallel: 1,
533
+ rationale: "Reserved for hard tasks; capped at 1 to bound cost."
534
+ }
535
+ ],
536
+ "kimi": [
537
+ {
538
+ model: "kimi-code/k3",
539
+ difficulty: ["medium", "difficult"],
540
+ maxParallel: 2,
541
+ rationale: "Independent second opinion on mid/hard work."
542
+ }
543
+ ],
544
+ "codex-cli": [
545
+ {
546
+ difficulty: ["medium", "difficult", "freeform"],
547
+ maxParallel: 2,
548
+ rationale: "Broad range including freeform; no model pin."
549
+ }
550
+ ],
551
+ "antigravity-cli": [
552
+ {
553
+ model: "Gemini 3.1 Pro (High)",
554
+ difficulty: ["easy"],
555
+ maxParallel: 2,
556
+ rationale: "Cheap capacity for easy tasks."
557
+ }
558
+ ],
559
+ "cursor-cli": [
560
+ {
561
+ model: "auto",
562
+ difficulty: ["easy"],
563
+ maxParallel: 1,
564
+ rationale: "Easy tasks only; auto model selection."
565
+ }
566
+ ],
567
+ "hermes-cli": [
568
+ {
569
+ difficulty: ["medium"],
570
+ maxParallel: 2,
571
+ provisional: true,
572
+ // NOTE: ESTIMATE, NOT OBSERVED. hermes-cli is absent from the live
573
+ // slot configuration this table was seeded from, so `medium` is a
574
+ // conservative placement rather than a transcription. Revisit once
575
+ // it has real usage data.
576
+ rationale: "ESTIMATE \u2014 no live slot to transcribe; conservative mid placement. Adjust after real use."
577
+ }
578
+ ]
579
+ });
580
+ var UNKNOWN_CLI_SLOT_RECIPE = Object.freeze({
581
+ difficulty: ["medium"],
582
+ maxParallel: 1,
583
+ provisional: true,
584
+ rationale: "Unrecognized provider \u2014 conservative default (medium, maxParallel 1). Review before relying on it."
585
+ });
586
+ function slotKey(slot) {
587
+ return [
588
+ slot.provider,
589
+ slot.model ?? "",
590
+ slot.thinkingLevel ?? "",
591
+ [...slot.difficulty ?? []].sort().join("|"),
592
+ [...slot.capability ?? []].sort().join("|"),
593
+ slot.maxParallel ?? ""
594
+ ].join("\0");
595
+ }
596
+ function dedupeDetected(detected) {
597
+ const seen = /* @__PURE__ */ new Set();
598
+ const out = [];
599
+ for (const d of detected) {
600
+ const type = typeof d?.type === "string" ? d.type.trim() : "";
601
+ if (!type || seen.has(type)) continue;
602
+ seen.add(type);
603
+ out.push({ ...d, type });
604
+ }
605
+ return out;
606
+ }
607
+ function buildSlotProposal(detected, currentSlots = []) {
608
+ const providers = dedupeDetected(detected ?? []);
609
+ const proposedSlots = [];
610
+ const entries = [];
611
+ const unknownProviders = [];
612
+ const provisionalProviders = [];
613
+ for (const provider of providers) {
614
+ const known = CLI_SLOT_RECIPES[provider.type];
615
+ const recipes = known ?? [UNKNOWN_CLI_SLOT_RECIPE];
616
+ const isUnknown = !known;
617
+ if (isUnknown) unknownProviders.push(provider.type);
618
+ let providerProvisional = false;
619
+ for (const recipe of recipes) {
620
+ const slot = normalizeNodeCapabilitySlot({
621
+ provider: provider.type,
622
+ model: recipe.model,
623
+ thinkingLevel: recipe.thinkingLevel,
624
+ difficulty: recipe.difficulty,
625
+ maxParallel: recipe.maxParallel
626
+ });
627
+ if (!slot) continue;
628
+ const provisional = recipe.provisional === true;
629
+ if (provisional) providerProvisional = true;
630
+ proposedSlots.push(slot);
631
+ entries.push({
632
+ slot,
633
+ unknownProvider: isUnknown,
634
+ provisional,
635
+ ...recipe.rationale ? { rationale: recipe.rationale } : {}
636
+ });
637
+ }
638
+ if (providerProvisional) provisionalProviders.push(provider.type);
639
+ }
640
+ const proposedKeys = new Set(proposedSlots.map(slotKey));
641
+ const droppedSlots = currentSlots.filter((slot) => !proposedKeys.has(slotKey(slot)));
642
+ const proposedProviders = new Set(proposedSlots.map((s) => s.provider));
643
+ const droppedProviders = [...new Set(
644
+ droppedSlots.map((s) => s.provider).filter((p) => !proposedProviders.has(p))
645
+ )];
646
+ return {
647
+ proposedSlots,
648
+ entries,
649
+ unknownProviders,
650
+ provisionalProviders,
651
+ droppedSlots,
652
+ droppedProviders,
653
+ destructive: droppedSlots.length > 0
654
+ };
655
+ }
656
+ function buildMagiPanelProposal(detected, opts = {}) {
657
+ const providers = dedupeDetected(detected ?? []);
658
+ const tableOrder = Object.keys(CLI_SLOT_RECIPES);
659
+ const rank = (type) => {
660
+ const i = tableOrder.indexOf(type);
661
+ return i === -1 ? Number.MAX_SAFE_INTEGER : i;
662
+ };
663
+ const ordered = [...providers].sort((a, b) => rank(a.type) - rank(b.type));
664
+ const limit = Number.isFinite(opts.maxSlots) && opts.maxSlots > 0 ? Math.floor(opts.maxSlots) : ordered.length;
665
+ return ordered.slice(0, limit).map((p) => ({
666
+ ...opts.nodeId ? { nodeId: opts.nodeId } : {},
667
+ provider: p.type
668
+ // A model is intentionally NOT pinned: the panel's job is cross-provider
669
+ // independence, and pinning models here would silently couple the panel
670
+ // to this table's cost assumptions rather than to review quality.
671
+ }));
672
+ }
673
+
514
674
  // src/interpolation.ts
515
675
  function interpolateArgs(args, context) {
516
676
  const result = {};
@@ -578,7 +738,8 @@ var CANONICAL_MESH_TOOL_NAMES = [
578
738
  "mesh_magi_kind_panel_set",
579
739
  "mesh_magi_kind_panel_list",
580
740
  "mesh_node_slots_set",
581
- "mesh_node_slots_list"
741
+ "mesh_node_slots_list",
742
+ "mesh_node_slots_propose"
582
743
  ];
583
744
  var CANONICAL_MESH_TOOL_COUNT = CANONICAL_MESH_TOOL_NAMES.length;
584
745
 
@@ -599,11 +760,15 @@ function stripStatusProbeMarker(args) {
599
760
  0 && (module.exports = {
600
761
  CANONICAL_MESH_TOOL_COUNT,
601
762
  CANONICAL_MESH_TOOL_NAMES,
763
+ CLI_SLOT_RECIPES,
602
764
  DEFAULT_DIFFICULTY_BRAINS,
603
765
  MAGI_RAW_ANSWER_CAP,
604
766
  MESH_TASK_DIFFICULTIES,
605
767
  STATUS_PROBE_ARG_KEY,
768
+ UNKNOWN_CLI_SLOT_RECIPE,
606
769
  argsCarryStatusProbeMarker,
770
+ buildMagiPanelProposal,
771
+ buildSlotProposal,
607
772
  canonicalDaemonId,
608
773
  daemonIdsEquivalent,
609
774
  deriveSlotsFromLegacy,