@versatly/workgraph 1.0.0 → 1.2.0

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.
@@ -3,12 +3,17 @@ import {
3
3
  allClaims,
4
4
  append,
5
5
  checkpoint,
6
+ claim,
6
7
  create,
7
8
  createRun,
8
9
  createThread,
10
+ done,
9
11
  historyOf,
10
12
  keywordSearch,
11
13
  list,
14
+ listReadyThreads,
15
+ listReadyThreadsInSpace,
16
+ listRuns,
12
17
  listTypes,
13
18
  loadPolicyRegistry,
14
19
  loadRegistry,
@@ -21,7 +26,7 @@ import {
21
26
  saveRegistry,
22
27
  stop,
23
28
  update
24
- } from "./chunk-R2MLGBHB.js";
29
+ } from "./chunk-G6B47IBD.js";
25
30
 
26
31
  // src/workspace.ts
27
32
  var workspace_exports = {};
@@ -298,7 +303,7 @@ function renderCommandCenter(input) {
298
303
  const claimsSection = [
299
304
  "## Active Claims",
300
305
  "",
301
- ...input.claims.length > 0 ? input.claims.map((claim) => `- ${claim.owner} -> \`${claim.target}\``) : ["- None"],
306
+ ...input.claims.length > 0 ? input.claims.map((claim2) => `- ${claim2.owner} -> \`${claim2.target}\``) : ["- None"],
302
307
  ""
303
308
  ];
304
309
  const blockedSection = [
@@ -532,14 +537,480 @@ function writeSkillManifest(workspacePath, slug, skill, actor) {
532
537
  fs4.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
533
538
  }
534
539
 
540
+ // src/lens.ts
541
+ var lens_exports = {};
542
+ __export(lens_exports, {
543
+ generateContextLens: () => generateContextLens,
544
+ listContextLenses: () => listContextLenses,
545
+ materializeContextLens: () => materializeContextLens
546
+ });
547
+ import fs5 from "fs";
548
+ import path5 from "path";
549
+ var DEFAULT_LOOKBACK_HOURS = 24;
550
+ var DEFAULT_STALE_HOURS = 24;
551
+ var DEFAULT_LIMIT = 10;
552
+ var PRIORITY_ORDER = {
553
+ urgent: 0,
554
+ high: 1,
555
+ medium: 2,
556
+ low: 3
557
+ };
558
+ var HIGH_RISK_PRIORITIES = /* @__PURE__ */ new Set(["urgent", "high"]);
559
+ var HIGH_RISK_SEVERITIES = /* @__PURE__ */ new Set(["sev0", "sev1", "sev2"]);
560
+ var BUILT_IN_LENSES = [
561
+ {
562
+ id: "my-work",
563
+ description: "Actor workload, blockers, stale claims, and ready-next queue"
564
+ },
565
+ {
566
+ id: "team-risk",
567
+ description: "High-risk blockers, stale active claims, failed runs, and incidents"
568
+ },
569
+ {
570
+ id: "customer-health",
571
+ description: "Customer-tagged delivery health, blockers, and related incidents"
572
+ },
573
+ {
574
+ id: "exec-brief",
575
+ description: "Top priorities, momentum, risks, and recent decisions"
576
+ }
577
+ ];
578
+ function listContextLenses() {
579
+ return BUILT_IN_LENSES.map((lens) => ({ ...lens }));
580
+ }
581
+ function generateContextLens(workspacePath, lensId, options = {}) {
582
+ const normalizedLensId = normalizeLensId(lensId);
583
+ const normalizedOptions = normalizeLensOptions(options);
584
+ switch (normalizedLensId) {
585
+ case "my-work":
586
+ return buildMyWorkLens(workspacePath, normalizedOptions);
587
+ case "team-risk":
588
+ return buildTeamRiskLens(workspacePath, normalizedOptions);
589
+ case "customer-health":
590
+ return buildCustomerHealthLens(workspacePath, normalizedOptions);
591
+ case "exec-brief":
592
+ return buildExecBriefLens(workspacePath, normalizedOptions);
593
+ default:
594
+ return assertNever(normalizedLensId);
595
+ }
596
+ }
597
+ function materializeContextLens(workspacePath, lensId, options) {
598
+ const result = generateContextLens(workspacePath, lensId, options);
599
+ const absOutputPath = resolvePathWithinWorkspace2(workspacePath, options.outputPath);
600
+ const relOutputPath = path5.relative(workspacePath, absOutputPath).replace(/\\/g, "/");
601
+ const parentDir = path5.dirname(absOutputPath);
602
+ if (!fs5.existsSync(parentDir)) fs5.mkdirSync(parentDir, { recursive: true });
603
+ const existed = fs5.existsSync(absOutputPath);
604
+ fs5.writeFileSync(absOutputPath, result.markdown, "utf-8");
605
+ append(
606
+ workspacePath,
607
+ options.actor ?? result.actor ?? "system",
608
+ existed ? "update" : "create",
609
+ relOutputPath,
610
+ "lens",
611
+ {
612
+ lens: result.lens,
613
+ sections: result.sections.length
614
+ }
615
+ );
616
+ return {
617
+ ...result,
618
+ outputPath: relOutputPath,
619
+ created: !existed
620
+ };
621
+ }
622
+ function buildMyWorkLens(workspacePath, options) {
623
+ const actor = options.actor;
624
+ const nowMs = Date.now();
625
+ const staleCutoffMs = nowMs - options.staleHours * 60 * 60 * 1e3;
626
+ const claims = [...allClaims(workspacePath).entries()];
627
+ const myClaimedThreads = claims.filter(([, owner]) => owner === actor).map(([target]) => read(workspacePath, target)).filter((instance) => !!instance && instance.type === "thread").sort(compareThreadsByPriorityThenUpdated);
628
+ const myBlockedThreads = myClaimedThreads.filter((instance) => String(instance.fields.status ?? "") === "blocked").slice(0, options.limit);
629
+ const staleClaims = myClaimedThreads.filter((instance) => isStale(instance, staleCutoffMs)).slice(0, options.limit);
630
+ const nextReady = listReadyThreads(workspacePath).filter((instance) => !instance.fields.owner).sort(compareThreadsByPriorityThenUpdated).slice(0, options.limit);
631
+ const sections = [
632
+ {
633
+ id: "my_claims",
634
+ title: `Claimed Threads (${actor})`,
635
+ items: myClaimedThreads.slice(0, options.limit).map((instance) => toThreadItem(instance, nowMs))
636
+ },
637
+ {
638
+ id: "my_blockers",
639
+ title: `Blocked Threads (${actor})`,
640
+ items: myBlockedThreads.map((instance) => toThreadItem(instance, nowMs))
641
+ },
642
+ {
643
+ id: "stale_claims",
644
+ title: `Stale Claims (${options.staleHours}h+)`,
645
+ items: staleClaims.map((instance) => toThreadItem(instance, nowMs))
646
+ },
647
+ {
648
+ id: "next_ready",
649
+ title: "Next Ready Threads",
650
+ items: nextReady.map((instance) => toThreadItem(instance, nowMs))
651
+ }
652
+ ];
653
+ return finalizeLensResult("my-work", {
654
+ actor,
655
+ options,
656
+ metrics: {
657
+ myClaims: myClaimedThreads.length,
658
+ blocked: myBlockedThreads.length,
659
+ staleClaims: staleClaims.length,
660
+ nextReady: nextReady.length
661
+ },
662
+ sections
663
+ });
664
+ }
665
+ function buildTeamRiskLens(workspacePath, options) {
666
+ const nowMs = Date.now();
667
+ const staleCutoffMs = nowMs - options.staleHours * 60 * 60 * 1e3;
668
+ const lookbackCutoffMs = nowMs - options.lookbackHours * 60 * 60 * 1e3;
669
+ const threads = list(workspacePath, "thread");
670
+ const blockedHighPriority = threads.filter((instance) => String(instance.fields.status ?? "") === "blocked").filter((instance) => HIGH_RISK_PRIORITIES.has(normalizePriority(instance.fields.priority))).sort(compareThreadsByPriorityThenUpdated).slice(0, options.limit);
671
+ const staleActiveClaims = [...allClaims(workspacePath).entries()].map(([target, owner]) => ({ owner, instance: read(workspacePath, target) })).filter((entry) => !!entry.instance && entry.instance.type === "thread").filter((entry) => String(entry.instance.fields.status ?? "") === "active").filter((entry) => isStale(entry.instance, staleCutoffMs)).slice(0, options.limit);
672
+ const failedRuns = listRuns(workspacePath, { status: "failed" }).filter((run) => parseTimestamp(run.updatedAt) >= lookbackCutoffMs).slice(0, options.limit);
673
+ const highSeverityIncidents = list(workspacePath, "incident").filter((incident) => String(incident.fields.status ?? "") === "active").filter((incident) => HIGH_RISK_SEVERITIES.has(normalizeSeverity(incident.fields.severity))).slice(0, options.limit);
674
+ const sections = [
675
+ {
676
+ id: "blocked_high_priority_threads",
677
+ title: "High Priority Blocked Threads",
678
+ items: blockedHighPriority.map((instance) => toThreadItem(instance, nowMs))
679
+ },
680
+ {
681
+ id: "stale_active_claims",
682
+ title: `Stale Active Claims (${options.staleHours}h+)`,
683
+ items: staleActiveClaims.map((entry) => ({
684
+ ...toThreadItem(entry.instance, nowMs),
685
+ owner: entry.owner
686
+ }))
687
+ },
688
+ {
689
+ id: "failed_runs",
690
+ title: `Failed Runs (${options.lookbackHours}h window)`,
691
+ items: failedRuns.map(toRunItem)
692
+ },
693
+ {
694
+ id: "active_high_severity_incidents",
695
+ title: "Active High-Severity Incidents",
696
+ items: highSeverityIncidents.map((incident) => toIncidentItem(incident, nowMs))
697
+ }
698
+ ];
699
+ return finalizeLensResult("team-risk", {
700
+ actor: options.actor,
701
+ options,
702
+ metrics: {
703
+ blockedHighPriority: blockedHighPriority.length,
704
+ staleActiveClaims: staleActiveClaims.length,
705
+ failedRuns: failedRuns.length,
706
+ activeHighSeverityIncidents: highSeverityIncidents.length
707
+ },
708
+ sections
709
+ });
710
+ }
711
+ function buildCustomerHealthLens(workspacePath, options) {
712
+ const nowMs = Date.now();
713
+ const customerThreads = list(workspacePath, "thread").filter(isCustomerLinked).sort(compareThreadsByPriorityThenUpdated);
714
+ const activeCustomerThreads = customerThreads.filter((instance) => ["open", "active"].includes(String(instance.fields.status ?? ""))).slice(0, options.limit);
715
+ const blockedCustomerThreads = customerThreads.filter((instance) => String(instance.fields.status ?? "") === "blocked").slice(0, options.limit);
716
+ const customerIncidents = list(workspacePath, "incident").filter((incident) => String(incident.fields.status ?? "") === "active").filter(isCustomerLinked).slice(0, options.limit);
717
+ const clients = list(workspacePath, "client").slice(0, options.limit);
718
+ const sections = [
719
+ {
720
+ id: "active_customer_threads",
721
+ title: "Active Customer Threads",
722
+ items: activeCustomerThreads.map((instance) => toThreadItem(instance, nowMs))
723
+ },
724
+ {
725
+ id: "blocked_customer_threads",
726
+ title: "Blocked Customer Threads",
727
+ items: blockedCustomerThreads.map((instance) => toThreadItem(instance, nowMs))
728
+ },
729
+ {
730
+ id: "customer_incidents",
731
+ title: "Customer Incidents",
732
+ items: customerIncidents.map((incident) => toIncidentItem(incident, nowMs))
733
+ },
734
+ {
735
+ id: "client_records",
736
+ title: "Client Records",
737
+ items: clients.map((instance) => ({
738
+ title: String(instance.fields.title ?? instance.path),
739
+ path: instance.path,
740
+ status: stringOrUndefined(instance.fields.status),
741
+ detail: stringOrUndefined(instance.fields.health ?? instance.fields.risk),
742
+ ageHours: ageHours(instance, nowMs)
743
+ }))
744
+ }
745
+ ];
746
+ return finalizeLensResult("customer-health", {
747
+ actor: options.actor,
748
+ options,
749
+ metrics: {
750
+ activeCustomerThreads: activeCustomerThreads.length,
751
+ blockedCustomerThreads: blockedCustomerThreads.length,
752
+ customerIncidents: customerIncidents.length,
753
+ clients: clients.length
754
+ },
755
+ sections
756
+ });
757
+ }
758
+ function buildExecBriefLens(workspacePath, options) {
759
+ const nowMs = Date.now();
760
+ const lookbackCutoffMs = nowMs - options.lookbackHours * 60 * 60 * 1e3;
761
+ const threads = list(workspacePath, "thread");
762
+ const topPriorities = threads.filter((instance) => ["open", "active"].includes(String(instance.fields.status ?? ""))).sort(compareThreadsByPriorityThenUpdated).slice(0, options.limit);
763
+ const momentum = threads.filter((instance) => String(instance.fields.status ?? "") === "done").filter((instance) => parseTimestamp(instance.fields.updated) >= lookbackCutoffMs).sort(compareThreadsByPriorityThenUpdated).slice(0, options.limit);
764
+ const blockedHighPriority = threads.filter((instance) => String(instance.fields.status ?? "") === "blocked").filter((instance) => HIGH_RISK_PRIORITIES.has(normalizePriority(instance.fields.priority))).sort(compareThreadsByPriorityThenUpdated).slice(0, options.limit);
765
+ const failedRuns = listRuns(workspacePath, { status: "failed" }).filter((run) => parseTimestamp(run.updatedAt) >= lookbackCutoffMs).slice(0, options.limit);
766
+ const decisions = list(workspacePath, "decision").filter((instance) => ["proposed", "approved", "active"].includes(String(instance.fields.status ?? ""))).filter((instance) => parseTimestamp(instance.fields.updated ?? instance.fields.date) >= lookbackCutoffMs).slice(0, options.limit);
767
+ const sections = [
768
+ {
769
+ id: "top_priorities",
770
+ title: "Top Priorities",
771
+ items: topPriorities.map((instance) => toThreadItem(instance, nowMs))
772
+ },
773
+ {
774
+ id: "momentum",
775
+ title: `Momentum (${options.lookbackHours}h completed)`,
776
+ items: momentum.map((instance) => toThreadItem(instance, nowMs))
777
+ },
778
+ {
779
+ id: "key_risks",
780
+ title: "Key Risks",
781
+ items: [
782
+ ...blockedHighPriority.map((instance) => toThreadItem(instance, nowMs)),
783
+ ...failedRuns.map(toRunItem)
784
+ ].slice(0, options.limit)
785
+ },
786
+ {
787
+ id: "recent_decisions",
788
+ title: `Decisions (${options.lookbackHours}h window)`,
789
+ items: decisions.map((instance) => ({
790
+ title: String(instance.fields.title ?? instance.path),
791
+ path: instance.path,
792
+ status: stringOrUndefined(instance.fields.status),
793
+ detail: stringOrUndefined(instance.fields.date),
794
+ ageHours: ageHours(instance, nowMs)
795
+ }))
796
+ }
797
+ ];
798
+ return finalizeLensResult("exec-brief", {
799
+ actor: options.actor,
800
+ options,
801
+ metrics: {
802
+ topPriorities: topPriorities.length,
803
+ momentumDone: momentum.length,
804
+ risks: blockedHighPriority.length + failedRuns.length,
805
+ decisions: decisions.length
806
+ },
807
+ sections
808
+ });
809
+ }
810
+ function finalizeLensResult(lens, input) {
811
+ const base = {
812
+ lens,
813
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
814
+ actor: input.actor,
815
+ options: {
816
+ lookbackHours: input.options.lookbackHours,
817
+ staleHours: input.options.staleHours,
818
+ limit: input.options.limit
819
+ },
820
+ metrics: input.metrics,
821
+ sections: input.sections
822
+ };
823
+ return {
824
+ ...base,
825
+ markdown: renderLensMarkdown(base)
826
+ };
827
+ }
828
+ function toThreadItem(instance, nowMs) {
829
+ return {
830
+ title: String(instance.fields.title ?? instance.path),
831
+ path: instance.path,
832
+ status: stringOrUndefined(instance.fields.status),
833
+ priority: stringOrUndefined(instance.fields.priority),
834
+ owner: stringOrUndefined(instance.fields.owner),
835
+ detail: renderThreadDependencies(instance),
836
+ ageHours: ageHours(instance, nowMs)
837
+ };
838
+ }
839
+ function toIncidentItem(instance, nowMs) {
840
+ return {
841
+ title: String(instance.fields.title ?? instance.path),
842
+ path: instance.path,
843
+ status: stringOrUndefined(instance.fields.status),
844
+ priority: stringOrUndefined(instance.fields.severity),
845
+ owner: stringOrUndefined(instance.fields.owner),
846
+ ageHours: ageHours(instance, nowMs)
847
+ };
848
+ }
849
+ function toRunItem(run) {
850
+ return {
851
+ title: run.objective,
852
+ path: `runs/${run.id}.md`,
853
+ status: run.status,
854
+ owner: run.actor,
855
+ detail: run.error ?? run.output,
856
+ ageHours: ageHoursFromIso(run.updatedAt)
857
+ };
858
+ }
859
+ function renderLensMarkdown(input) {
860
+ const lines = [
861
+ `# Workgraph Context Lens: ${input.lens}`,
862
+ "",
863
+ `Generated: ${input.generatedAt}`,
864
+ ...input.actor ? [`Actor: ${input.actor}`] : [],
865
+ `Lookback: ${input.options.lookbackHours}h`,
866
+ `Stale threshold: ${input.options.staleHours}h`,
867
+ `Section limit: ${input.options.limit}`,
868
+ "",
869
+ "## Metrics",
870
+ "",
871
+ ...Object.entries(input.metrics).map(([metric, value]) => `- ${metric}: ${value}`),
872
+ ""
873
+ ];
874
+ for (const section of input.sections) {
875
+ lines.push(`## ${section.title}`);
876
+ lines.push("");
877
+ if (section.items.length === 0) {
878
+ lines.push("- None");
879
+ lines.push("");
880
+ continue;
881
+ }
882
+ for (const item of section.items) {
883
+ lines.push(`- ${renderLensItem(item)}`);
884
+ }
885
+ lines.push("");
886
+ }
887
+ return lines.join("\n");
888
+ }
889
+ function renderLensItem(item) {
890
+ const components = [item.title];
891
+ if (item.path) components.push(`(\`${item.path}\`)`);
892
+ const metadata = [];
893
+ if (item.status) metadata.push(`status=${item.status}`);
894
+ if (item.priority) metadata.push(`priority=${item.priority}`);
895
+ if (item.owner) metadata.push(`owner=${item.owner}`);
896
+ if (typeof item.ageHours === "number") metadata.push(`age=${item.ageHours.toFixed(1)}h`);
897
+ if (metadata.length > 0) components.push(`[${metadata.join(", ")}]`);
898
+ if (item.detail) components.push(`- ${item.detail}`);
899
+ return components.join(" ");
900
+ }
901
+ function resolvePathWithinWorkspace2(workspacePath, outputPath) {
902
+ const base = path5.resolve(workspacePath);
903
+ const resolved = path5.resolve(base, outputPath);
904
+ if (!resolved.startsWith(base + path5.sep) && resolved !== base) {
905
+ throw new Error(`Invalid lens output path: ${outputPath}`);
906
+ }
907
+ return resolved;
908
+ }
909
+ function normalizeLensId(value) {
910
+ const normalized = String(value).trim().toLowerCase().replace(/^lens:\/\//, "");
911
+ if (normalized === "my-work") return "my-work";
912
+ if (normalized === "team-risk") return "team-risk";
913
+ if (normalized === "customer-health") return "customer-health";
914
+ if (normalized === "exec-brief") return "exec-brief";
915
+ const valid = BUILT_IN_LENSES.map((item) => item.id).join(", ");
916
+ throw new Error(`Unknown context lens "${value}". Valid lenses: ${valid}`);
917
+ }
918
+ function normalizeLensOptions(options) {
919
+ return {
920
+ actor: String(options.actor ?? "anonymous").trim() || "anonymous",
921
+ lookbackHours: parsePositiveNumber(options.lookbackHours, "lookbackHours", DEFAULT_LOOKBACK_HOURS),
922
+ staleHours: parsePositiveNumber(options.staleHours, "staleHours", DEFAULT_STALE_HOURS),
923
+ limit: parsePositiveInteger(options.limit, "limit", DEFAULT_LIMIT)
924
+ };
925
+ }
926
+ function parsePositiveNumber(value, fieldName, defaultValue) {
927
+ if (value === void 0 || value === null) return defaultValue;
928
+ const parsed = Number(value);
929
+ if (!Number.isFinite(parsed) || parsed <= 0) {
930
+ throw new Error(`Invalid ${fieldName}: expected a positive number.`);
931
+ }
932
+ return parsed;
933
+ }
934
+ function parsePositiveInteger(value, fieldName, defaultValue) {
935
+ if (value === void 0 || value === null) return defaultValue;
936
+ const parsed = Number.parseInt(String(value), 10);
937
+ if (!Number.isInteger(parsed) || parsed <= 0) {
938
+ throw new Error(`Invalid ${fieldName}: expected a positive integer.`);
939
+ }
940
+ return parsed;
941
+ }
942
+ function compareThreadsByPriorityThenUpdated(a, b) {
943
+ const priorityDelta = rankPriority(a) - rankPriority(b);
944
+ if (priorityDelta !== 0) return priorityDelta;
945
+ return parseTimestamp(b.fields.updated) - parseTimestamp(a.fields.updated);
946
+ }
947
+ function rankPriority(instance) {
948
+ const priority = normalizePriority(instance.fields.priority);
949
+ return PRIORITY_ORDER[priority] ?? PRIORITY_ORDER.medium;
950
+ }
951
+ function normalizePriority(value) {
952
+ return String(value ?? "medium").trim().toLowerCase();
953
+ }
954
+ function normalizeSeverity(value) {
955
+ return String(value ?? "sev4").trim().toLowerCase();
956
+ }
957
+ function isStale(instance, staleCutoffMs) {
958
+ const updatedAt = parseTimestamp(instance.fields.updated ?? instance.fields.created);
959
+ if (!Number.isFinite(updatedAt)) return false;
960
+ return updatedAt <= staleCutoffMs;
961
+ }
962
+ function parseTimestamp(value) {
963
+ const parsed = Date.parse(String(value ?? ""));
964
+ return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
965
+ }
966
+ function ageHours(instance, nowMs) {
967
+ const updatedAt = parseTimestamp(instance.fields.updated ?? instance.fields.created);
968
+ if (!Number.isFinite(updatedAt)) return void 0;
969
+ return Math.max(0, (nowMs - updatedAt) / (60 * 60 * 1e3));
970
+ }
971
+ function ageHoursFromIso(value) {
972
+ const nowMs = Date.now();
973
+ const ts = parseTimestamp(value);
974
+ if (!Number.isFinite(ts)) return void 0;
975
+ return Math.max(0, (nowMs - ts) / (60 * 60 * 1e3));
976
+ }
977
+ function renderThreadDependencies(instance) {
978
+ const deps = instance.fields.deps;
979
+ if (!Array.isArray(deps) || deps.length === 0) return void 0;
980
+ const visible = deps.slice(0, 3).map((value) => String(value));
981
+ const suffix = deps.length > visible.length ? ` +${deps.length - visible.length} more` : "";
982
+ return `deps: ${visible.join(", ")}${suffix}`;
983
+ }
984
+ function isCustomerLinked(instance) {
985
+ const tags = normalizeTags(instance.fields.tags);
986
+ if (tags.includes("customer") || tags.includes("client")) return true;
987
+ const candidateFields = ["client", "client_ref", "customer", "customer_ref", "account", "account_ref"];
988
+ return candidateFields.some((key) => {
989
+ const value = instance.fields[key];
990
+ return typeof value === "string" && value.trim().length > 0;
991
+ });
992
+ }
993
+ function normalizeTags(value) {
994
+ if (!Array.isArray(value)) return [];
995
+ return value.map((item) => String(item).trim().toLowerCase()).filter(Boolean);
996
+ }
997
+ function stringOrUndefined(value) {
998
+ if (value === void 0 || value === null) return void 0;
999
+ const normalized = String(value).trim();
1000
+ return normalized.length > 0 ? normalized : void 0;
1001
+ }
1002
+ function assertNever(value) {
1003
+ throw new Error(`Unhandled lens variant: ${String(value)}`);
1004
+ }
1005
+
535
1006
  // src/board.ts
536
1007
  var board_exports = {};
537
1008
  __export(board_exports, {
538
1009
  generateKanbanBoard: () => generateKanbanBoard,
539
1010
  syncKanbanBoard: () => syncKanbanBoard
540
1011
  });
541
- import fs5 from "fs";
542
- import path5 from "path";
1012
+ import fs6 from "fs";
1013
+ import path6 from "path";
543
1014
  function generateKanbanBoard(workspacePath, options = {}) {
544
1015
  const threads = list(workspacePath, "thread");
545
1016
  const grouped = groupThreads(threads);
@@ -555,12 +1026,12 @@ function generateKanbanBoard(workspacePath, options = {}) {
555
1026
  }
556
1027
  const content = renderKanbanMarkdown(lanes);
557
1028
  const relOutputPath = options.outputPath ?? "ops/Workgraph Board.md";
558
- const absOutputPath = resolvePathWithinWorkspace2(workspacePath, relOutputPath);
559
- const parentDir = path5.dirname(absOutputPath);
560
- if (!fs5.existsSync(parentDir)) fs5.mkdirSync(parentDir, { recursive: true });
561
- fs5.writeFileSync(absOutputPath, content, "utf-8");
1029
+ const absOutputPath = resolvePathWithinWorkspace3(workspacePath, relOutputPath);
1030
+ const parentDir = path6.dirname(absOutputPath);
1031
+ if (!fs6.existsSync(parentDir)) fs6.mkdirSync(parentDir, { recursive: true });
1032
+ fs6.writeFileSync(absOutputPath, content, "utf-8");
562
1033
  return {
563
- outputPath: path5.relative(workspacePath, absOutputPath).replace(/\\/g, "/"),
1034
+ outputPath: path6.relative(workspacePath, absOutputPath).replace(/\\/g, "/"),
564
1035
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
565
1036
  counts: {
566
1037
  backlog: grouped.open.length,
@@ -658,10 +1129,10 @@ function renderKanbanMarkdown(lanes) {
658
1129
  lines.push("");
659
1130
  return lines.join("\n");
660
1131
  }
661
- function resolvePathWithinWorkspace2(workspacePath, outputPath) {
662
- const base = path5.resolve(workspacePath);
663
- const resolved = path5.resolve(base, outputPath);
664
- if (!resolved.startsWith(base + path5.sep) && resolved !== base) {
1132
+ function resolvePathWithinWorkspace3(workspacePath, outputPath) {
1133
+ const base = path6.resolve(workspacePath);
1134
+ const resolved = path6.resolve(base, outputPath);
1135
+ if (!resolved.startsWith(base + path6.sep) && resolved !== base) {
665
1136
  throw new Error(`Invalid board output path: ${outputPath}`);
666
1137
  }
667
1138
  return resolved;
@@ -1142,6 +1613,311 @@ function supportedIntegrationList() {
1142
1613
  return Object.keys(INTEGRATIONS).sort().join(", ");
1143
1614
  }
1144
1615
 
1616
+ // src/swarm.ts
1617
+ var swarm_exports = {};
1618
+ __export(swarm_exports, {
1619
+ createPlanTemplate: () => createPlanTemplate,
1620
+ deployPlan: () => deployPlan,
1621
+ getSwarmStatus: () => getSwarmStatus,
1622
+ synthesize: () => synthesize,
1623
+ validatePlan: () => validatePlan,
1624
+ workerClaim: () => workerClaim,
1625
+ workerComplete: () => workerComplete,
1626
+ workerLoop: () => workerLoop
1627
+ });
1628
+ import * as fs7 from "fs";
1629
+ import * as path7 from "path";
1630
+ function createPlanTemplate(goal) {
1631
+ return {
1632
+ goal,
1633
+ tasks: [],
1634
+ phases: [],
1635
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1636
+ estimatedTotalMinutes: 0
1637
+ };
1638
+ }
1639
+ function validatePlan(plan) {
1640
+ const errors = [];
1641
+ if (!plan.goal.title) errors.push("Goal title is required");
1642
+ if (plan.tasks.length === 0) errors.push("Plan has no tasks");
1643
+ if (plan.tasks.length > (plan.goal.maxTasks ?? 1e3)) {
1644
+ errors.push(`Plan has ${plan.tasks.length} tasks, exceeds max ${plan.goal.maxTasks ?? 1e3}`);
1645
+ }
1646
+ const taskTitles = new Set(plan.tasks.map((t) => t.title));
1647
+ for (const task of plan.tasks) {
1648
+ for (const dep of task.dependsOn ?? []) {
1649
+ if (!taskTitles.has(dep)) {
1650
+ errors.push(`Task "${task.title}" depends on unknown task "${dep}"`);
1651
+ }
1652
+ }
1653
+ }
1654
+ const visited = /* @__PURE__ */ new Set();
1655
+ const stack = /* @__PURE__ */ new Set();
1656
+ const depMap = /* @__PURE__ */ new Map();
1657
+ for (const task of plan.tasks) {
1658
+ depMap.set(task.title, task.dependsOn ?? []);
1659
+ }
1660
+ function hasCycle(node) {
1661
+ if (stack.has(node)) return true;
1662
+ if (visited.has(node)) return false;
1663
+ visited.add(node);
1664
+ stack.add(node);
1665
+ for (const dep of depMap.get(node) ?? []) {
1666
+ if (hasCycle(dep)) return true;
1667
+ }
1668
+ stack.delete(node);
1669
+ return false;
1670
+ }
1671
+ for (const task of plan.tasks) {
1672
+ visited.clear();
1673
+ stack.clear();
1674
+ if (hasCycle(task.title)) {
1675
+ errors.push(`Circular dependency detected involving "${task.title}"`);
1676
+ break;
1677
+ }
1678
+ }
1679
+ for (const phase of plan.phases) {
1680
+ for (const idx of phase.taskIndices) {
1681
+ if (idx < 0 || idx >= plan.tasks.length) {
1682
+ errors.push(`Phase "${phase.name}" references invalid task index ${idx}`);
1683
+ }
1684
+ }
1685
+ }
1686
+ return { valid: errors.length === 0, errors };
1687
+ }
1688
+ function deployPlan(workspacePath, plan, actor) {
1689
+ const validation = validatePlan(plan);
1690
+ if (!validation.valid) {
1691
+ throw new Error(`Invalid plan: ${validation.errors.join("; ")}`);
1692
+ }
1693
+ const spaceSlug = slugify(`swarm-${plan.goal.title}`);
1694
+ const spacePath = path7.join("spaces", `${spaceSlug}.md`);
1695
+ const spaceFullPath = path7.join(workspacePath, spacePath);
1696
+ if (!fs7.existsSync(spaceFullPath)) {
1697
+ const spaceDir = path7.join(workspacePath, "spaces");
1698
+ fs7.mkdirSync(spaceDir, { recursive: true });
1699
+ const spaceFrontmatter = [
1700
+ "---",
1701
+ `title: "Swarm: ${plan.goal.title}"`,
1702
+ `status: active`,
1703
+ `created: '${(/* @__PURE__ */ new Date()).toISOString()}'`,
1704
+ `updated: '${(/* @__PURE__ */ new Date()).toISOString()}'`,
1705
+ "---",
1706
+ "",
1707
+ `# Swarm Space: ${plan.goal.title}`,
1708
+ "",
1709
+ plan.goal.description,
1710
+ "",
1711
+ `Total tasks: ${plan.tasks.length}`
1712
+ ].join("\n");
1713
+ fs7.writeFileSync(spaceFullPath, spaceFrontmatter);
1714
+ }
1715
+ const threadPaths = [];
1716
+ const slugMap = /* @__PURE__ */ new Map();
1717
+ for (const task of plan.tasks) {
1718
+ const taskSlug = slugify(task.title);
1719
+ slugMap.set(task.title, taskSlug);
1720
+ }
1721
+ for (const task of plan.tasks) {
1722
+ const taskSlug = slugMap.get(task.title);
1723
+ let body = `# ${task.title}
1724
+
1725
+ ${task.description}
1726
+ `;
1727
+ if (task.dependsOn && task.dependsOn.length > 0) {
1728
+ body += `
1729
+ ## Dependencies
1730
+ `;
1731
+ for (const dep of task.dependsOn) {
1732
+ const depSlug = slugMap.get(dep);
1733
+ if (depSlug) {
1734
+ body += `- [[${depSlug}]]
1735
+ `;
1736
+ }
1737
+ }
1738
+ }
1739
+ body += `
1740
+ ## Output
1741
+
1742
+ _Agent writes result here._
1743
+ `;
1744
+ if (task.tags && task.tags.length > 0) {
1745
+ body += `
1746
+ Tags: ${task.tags.join(", ")}
1747
+ `;
1748
+ }
1749
+ const created = createThread(workspacePath, task.title, body, actor, {
1750
+ priority: task.priority,
1751
+ space: `spaces/${spaceSlug}`
1752
+ });
1753
+ threadPaths.push(created.path);
1754
+ }
1755
+ const deployment = {
1756
+ planPath: path7.join(".workgraph", `swarm-${spaceSlug}.json`),
1757
+ workspacePath,
1758
+ threadPaths,
1759
+ spaceSlug,
1760
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1761
+ status: "deployed"
1762
+ };
1763
+ const manifestPath = path7.join(workspacePath, deployment.planPath);
1764
+ fs7.mkdirSync(path7.dirname(manifestPath), { recursive: true });
1765
+ fs7.writeFileSync(manifestPath, JSON.stringify({ plan, deployment }, null, 2));
1766
+ append(workspacePath, actor, "create", deployment.planPath, "swarm");
1767
+ return deployment;
1768
+ }
1769
+ function getSwarmStatus(workspacePath, spaceSlug) {
1770
+ const manifestPath = path7.join(workspacePath, ".workgraph", `swarm-${spaceSlug}.json`);
1771
+ if (!fs7.existsSync(manifestPath)) {
1772
+ throw new Error(`No swarm deployment found for space "${spaceSlug}"`);
1773
+ }
1774
+ const manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf-8"));
1775
+ const deployment = manifest.deployment;
1776
+ const threads = [];
1777
+ let claimed = 0;
1778
+ let done2 = 0;
1779
+ let blocked = 0;
1780
+ let open = 0;
1781
+ for (const threadPath of deployment.threadPaths) {
1782
+ const t = read(workspacePath, threadPath);
1783
+ if (!t) continue;
1784
+ const status = String(t.fields.status ?? "open");
1785
+ const threadInfo = {
1786
+ path: threadPath,
1787
+ title: String(t.fields.title ?? ""),
1788
+ status,
1789
+ owner: t.fields.owner ? String(t.fields.owner) : void 0,
1790
+ priority: String(t.fields.priority ?? "medium")
1791
+ };
1792
+ threads.push(threadInfo);
1793
+ if (status === "done") done2++;
1794
+ else if (status === "active") claimed++;
1795
+ else if (status === "blocked") blocked++;
1796
+ else open++;
1797
+ }
1798
+ const total = deployment.threadPaths.length;
1799
+ const readyToClaim = open;
1800
+ const percentComplete = total > 0 ? Math.round(done2 / total * 100) : 0;
1801
+ if (done2 === total) deployment.status = "done";
1802
+ else if (claimed > 0 || done2 > 0) deployment.status = "running";
1803
+ return {
1804
+ deployment,
1805
+ total,
1806
+ claimed,
1807
+ done: done2,
1808
+ blocked,
1809
+ open,
1810
+ readyToClaim,
1811
+ percentComplete,
1812
+ threads
1813
+ };
1814
+ }
1815
+ function workerClaim(workspacePath, spaceSlug, agent) {
1816
+ const ready = listReadyThreadsInSpace(workspacePath, `spaces/${spaceSlug}`);
1817
+ if (ready.length === 0) return null;
1818
+ const priorityOrder = {
1819
+ critical: 0,
1820
+ high: 1,
1821
+ medium: 2,
1822
+ low: 3
1823
+ };
1824
+ ready.sort((a, b) => {
1825
+ const aPri = priorityOrder[String(a.fields.priority)] ?? 2;
1826
+ const bPri = priorityOrder[String(b.fields.priority)] ?? 2;
1827
+ return aPri - bPri;
1828
+ });
1829
+ const target = ready[0];
1830
+ return claim(workspacePath, target.path, agent);
1831
+ }
1832
+ function workerComplete(workspacePath, threadPath, agent, result) {
1833
+ const t = read(workspacePath, threadPath);
1834
+ if (!t) throw new Error(`Thread not found: ${threadPath}`);
1835
+ const currentBody = t.body ?? "";
1836
+ const updatedBody = currentBody.replace(
1837
+ "_Agent writes result here._",
1838
+ result
1839
+ );
1840
+ return done(workspacePath, threadPath, agent, updatedBody);
1841
+ }
1842
+ async function workerLoop(workspacePath, spaceSlug, agent, workFn, options) {
1843
+ let completed = 0;
1844
+ let errors = 0;
1845
+ const maxTasks = options?.maxTasks ?? Infinity;
1846
+ const delayMs = options?.delayMs ?? 1e3;
1847
+ while (completed + errors < maxTasks) {
1848
+ const claimed = workerClaim(workspacePath, spaceSlug, agent);
1849
+ if (!claimed) break;
1850
+ try {
1851
+ const result = await workFn(claimed);
1852
+ workerComplete(workspacePath, claimed.path, agent, result);
1853
+ completed++;
1854
+ } catch (err) {
1855
+ errors++;
1856
+ const errorMsg = err instanceof Error ? err.message : String(err);
1857
+ try {
1858
+ update(workspacePath, claimed.path, {
1859
+ status: "blocked"
1860
+ }, `Error: ${errorMsg}`, agent);
1861
+ } catch {
1862
+ }
1863
+ }
1864
+ if (delayMs > 0) {
1865
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1866
+ }
1867
+ }
1868
+ return { completed, errors };
1869
+ }
1870
+ function synthesize(workspacePath, spaceSlug) {
1871
+ const status = getSwarmStatus(workspacePath, spaceSlug);
1872
+ const sections = [];
1873
+ const manifestPath = path7.join(workspacePath, ".workgraph", `swarm-${spaceSlug}.json`);
1874
+ const manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf-8"));
1875
+ const plan = manifest.plan;
1876
+ sections.push(`# ${plan.goal.title}
1877
+ `);
1878
+ sections.push(`${plan.goal.description}
1879
+ `);
1880
+ sections.push(`---
1881
+ `);
1882
+ for (const threadInfo of status.threads) {
1883
+ const t = read(workspacePath, threadInfo.path);
1884
+ if (!t) continue;
1885
+ if (threadInfo.status !== "done") {
1886
+ sections.push(`## [PENDING] ${threadInfo.title}
1887
+
1888
+ _Not yet completed._
1889
+ `);
1890
+ continue;
1891
+ }
1892
+ const body = t.body ?? "";
1893
+ const result = body.replace(/^#\s+.*\n/, "").trim();
1894
+ if (result && result !== "_Agent writes result here._") {
1895
+ sections.push(`## ${threadInfo.title}
1896
+
1897
+ ${result}
1898
+ `);
1899
+ } else {
1900
+ sections.push(`## ${threadInfo.title}
1901
+
1902
+ _Completed but no output found._
1903
+ `);
1904
+ }
1905
+ }
1906
+ sections.push(`
1907
+ ---
1908
+ `);
1909
+ sections.push(`*Generated from swarm "${plan.goal.title}" \u2014 ${status.done}/${status.total} tasks completed.*
1910
+ `);
1911
+ return {
1912
+ markdown: sections.join("\n"),
1913
+ completedCount: status.done,
1914
+ totalCount: status.total
1915
+ };
1916
+ }
1917
+ function slugify(text) {
1918
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 80);
1919
+ }
1920
+
1145
1921
  // src/diagnostics/index.ts
1146
1922
  var diagnostics_exports = {};
1147
1923
  __export(diagnostics_exports, {
@@ -1157,8 +1933,8 @@ __export(diagnostics_exports, {
1157
1933
  });
1158
1934
 
1159
1935
  // src/diagnostics/doctor.ts
1160
- import fs6 from "fs";
1161
- import path7 from "path";
1936
+ import fs8 from "fs";
1937
+ import path9 from "path";
1162
1938
  import YAML2 from "yaml";
1163
1939
 
1164
1940
  // src/diagnostics/format.ts
@@ -1222,7 +1998,7 @@ function inferPrimitiveTypeFromPath(targetPath) {
1222
1998
  }
1223
1999
 
1224
2000
  // src/diagnostics/primitives.ts
1225
- import path6 from "path";
2001
+ import path8 from "path";
1226
2002
  function loadPrimitiveInventory(workspacePath) {
1227
2003
  const registry = loadRegistry(workspacePath);
1228
2004
  const allPrimitives = queryPrimitives(workspacePath);
@@ -1240,7 +2016,7 @@ function loadPrimitiveInventory(workspacePath) {
1240
2016
  const requiredFields = Object.entries(typeDef?.fields ?? {}).filter(([, fieldDef]) => fieldDef.required === true).map(([fieldName]) => fieldName);
1241
2017
  const presentCount = requiredFields.filter((fieldName) => hasRequiredValue(instance.fields[fieldName])).length;
1242
2018
  const frontmatterCompleteness = requiredFields.length === 0 ? 1 : presentCount / requiredFields.length;
1243
- const slug = path6.basename(instance.path, ".md");
2019
+ const slug = path8.basename(instance.path, ".md");
1244
2020
  return {
1245
2021
  ...instance,
1246
2022
  slug,
@@ -1582,8 +2358,8 @@ function collectStaleRuns(workspacePath, staleAfterMs, now) {
1582
2358
  }
1583
2359
  function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
1584
2360
  const issues = [];
1585
- const manifestPath = path7.join(workspacePath, ".workgraph", "primitive-registry.yaml");
1586
- if (!fs6.existsSync(manifestPath)) {
2361
+ const manifestPath = path9.join(workspacePath, ".workgraph", "primitive-registry.yaml");
2362
+ if (!fs8.existsSync(manifestPath)) {
1587
2363
  issues.push({
1588
2364
  code: "broken-primitive-registry-reference",
1589
2365
  severity: "error",
@@ -1594,7 +2370,7 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
1594
2370
  }
1595
2371
  let parsed;
1596
2372
  try {
1597
- parsed = YAML2.parse(fs6.readFileSync(manifestPath, "utf-8"));
2373
+ parsed = YAML2.parse(fs8.readFileSync(manifestPath, "utf-8"));
1598
2374
  } catch (error) {
1599
2375
  issues.push({
1600
2376
  code: "broken-primitive-registry-reference",
@@ -1646,7 +2422,7 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
1646
2422
  path: ".workgraph/primitive-registry.yaml"
1647
2423
  });
1648
2424
  }
1649
- if (!fs6.existsSync(path7.join(workspacePath, directory))) {
2425
+ if (!fs8.existsSync(path9.join(workspacePath, directory))) {
1650
2426
  issues.push({
1651
2427
  code: "broken-primitive-registry-reference",
1652
2428
  severity: "error",
@@ -1681,8 +2457,8 @@ function collectPrimitiveRegistryReferenceIssues(workspacePath, inventory) {
1681
2457
  function collectEmptyPrimitiveDirectoryIssues(workspacePath, inventory) {
1682
2458
  const issues = [];
1683
2459
  for (const typeDef of inventory.typeDefs.values()) {
1684
- const directoryPath = path7.join(workspacePath, typeDef.directory);
1685
- if (!fs6.existsSync(directoryPath)) continue;
2460
+ const directoryPath = path9.join(workspacePath, typeDef.directory);
2461
+ if (!fs8.existsSync(directoryPath)) continue;
1686
2462
  const markdownCount = listMarkdownFilesRecursive(directoryPath).length;
1687
2463
  if (markdownCount > 0) continue;
1688
2464
  issues.push({
@@ -1711,10 +2487,10 @@ function removeOrphanLinks(workspacePath, orphanLinks) {
1711
2487
  }
1712
2488
  let removedLinks = 0;
1713
2489
  for (const [sourcePath, tokenSet] of tokensBySource.entries()) {
1714
- const absPath = path7.join(workspacePath, sourcePath);
1715
- if (!fs6.existsSync(absPath)) continue;
2490
+ const absPath = path9.join(workspacePath, sourcePath);
2491
+ if (!fs8.existsSync(absPath)) continue;
1716
2492
  try {
1717
- const raw = fs6.readFileSync(absPath, "utf-8");
2493
+ const raw = fs8.readFileSync(absPath, "utf-8");
1718
2494
  let fileRemoved = 0;
1719
2495
  const updated = raw.replace(/\[\[([^[\]]+)\]\]/g, (token) => {
1720
2496
  if (!tokenSet.has(token)) return token;
@@ -1722,7 +2498,7 @@ function removeOrphanLinks(workspacePath, orphanLinks) {
1722
2498
  return "";
1723
2499
  });
1724
2500
  if (fileRemoved === 0) continue;
1725
- fs6.writeFileSync(absPath, updated, "utf-8");
2501
+ fs8.writeFileSync(absPath, updated, "utf-8");
1726
2502
  removedLinks += fileRemoved;
1727
2503
  filesUpdated.push(sourcePath);
1728
2504
  } catch (error) {
@@ -1787,10 +2563,10 @@ function cancelStaleRuns(workspacePath, staleRuns, actor) {
1787
2563
  return { cancelled, errors };
1788
2564
  }
1789
2565
  function readDispatchRunsSnapshot(workspacePath) {
1790
- const runsPath = path7.join(workspacePath, ".workgraph", "dispatch-runs.json");
1791
- if (!fs6.existsSync(runsPath)) return [];
2566
+ const runsPath = path9.join(workspacePath, ".workgraph", "dispatch-runs.json");
2567
+ if (!fs8.existsSync(runsPath)) return [];
1792
2568
  try {
1793
- const parsed = JSON.parse(fs6.readFileSync(runsPath, "utf-8"));
2569
+ const parsed = JSON.parse(fs8.readFileSync(runsPath, "utf-8"));
1794
2570
  return Array.isArray(parsed.runs) ? parsed.runs : [];
1795
2571
  } catch {
1796
2572
  return [];
@@ -1801,9 +2577,9 @@ function listMarkdownFilesRecursive(rootDirectory) {
1801
2577
  const stack = [rootDirectory];
1802
2578
  while (stack.length > 0) {
1803
2579
  const current = stack.pop();
1804
- const entries = fs6.readdirSync(current, { withFileTypes: true });
2580
+ const entries = fs8.readdirSync(current, { withFileTypes: true });
1805
2581
  for (const entry of entries) {
1806
- const absPath = path7.join(current, entry.name);
2582
+ const absPath = path9.join(current, entry.name);
1807
2583
  if (entry.isDirectory()) {
1808
2584
  stack.push(absPath);
1809
2585
  continue;
@@ -1947,7 +2723,7 @@ function toNullableString(value) {
1947
2723
  }
1948
2724
 
1949
2725
  // src/diagnostics/viz.ts
1950
- import path8 from "path";
2726
+ import path10 from "path";
1951
2727
  var TYPE_COLORS = ["cyan", "magenta", "yellow", "green", "blue", "red"];
1952
2728
  function visualizeVaultGraph(workspacePath, options = {}) {
1953
2729
  const inventory = loadPrimitiveInventory(workspacePath);
@@ -2087,7 +2863,7 @@ function resolveFocusPath(focusInput, inventory) {
2087
2863
  const directCandidate = normalized.endsWith(".md") ? normalized : `${normalized}.md`;
2088
2864
  if (inventory.byPath.has(normalized)) return normalized;
2089
2865
  if (inventory.byPath.has(directCandidate)) return directCandidate;
2090
- const slug = path8.basename(normalized, ".md");
2866
+ const slug = path10.basename(normalized, ".md");
2091
2867
  const candidates = inventory.slugToPaths.get(slug) ?? [];
2092
2868
  if (candidates.length === 1) return candidates[0];
2093
2869
  if (candidates.length > 1) {
@@ -2395,8 +3171,8 @@ __export(autonomy_daemon_exports, {
2395
3171
  startAutonomyDaemon: () => startAutonomyDaemon,
2396
3172
  stopAutonomyDaemon: () => stopAutonomyDaemon
2397
3173
  });
2398
- import fs7 from "fs";
2399
- import path9 from "path";
3174
+ import fs9 from "fs";
3175
+ import path11 from "path";
2400
3176
  import { spawn } from "child_process";
2401
3177
  var DAEMON_DIR = ".workgraph/daemon";
2402
3178
  var AUTONOMY_PID_FILE = "autonomy.pid";
@@ -2405,29 +3181,29 @@ var AUTONOMY_LOG_FILE = "autonomy.log";
2405
3181
  var AUTONOMY_META_FILE = "autonomy-process.json";
2406
3182
  function startAutonomyDaemon(workspacePath, input) {
2407
3183
  const daemonDir = ensureDaemonDir(workspacePath);
2408
- const pidPath = path9.join(daemonDir, AUTONOMY_PID_FILE);
2409
- const heartbeatPath = input.heartbeatPath ? resolvePathWithinWorkspace3(workspacePath, input.heartbeatPath) : path9.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
2410
- const logPath = input.logPath ? resolvePathWithinWorkspace3(workspacePath, input.logPath) : path9.join(daemonDir, AUTONOMY_LOG_FILE);
2411
- const metaPath = path9.join(daemonDir, AUTONOMY_META_FILE);
3184
+ const pidPath = path11.join(daemonDir, AUTONOMY_PID_FILE);
3185
+ const heartbeatPath = input.heartbeatPath ? resolvePathWithinWorkspace4(workspacePath, input.heartbeatPath) : path11.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
3186
+ const logPath = input.logPath ? resolvePathWithinWorkspace4(workspacePath, input.logPath) : path11.join(daemonDir, AUTONOMY_LOG_FILE);
3187
+ const metaPath = path11.join(daemonDir, AUTONOMY_META_FILE);
2412
3188
  const existing = readAutonomyDaemonStatus(workspacePath, { cleanupStalePidFile: true });
2413
3189
  if (existing.running) {
2414
3190
  throw new Error(`Autonomy daemon already running (pid=${existing.pid}). Stop it before starting a new one.`);
2415
3191
  }
2416
- const logFd = fs7.openSync(logPath, "a");
3192
+ const logFd = fs9.openSync(logPath, "a");
2417
3193
  const args = buildAutonomyDaemonArgs(workspacePath, input, heartbeatPath);
2418
3194
  const child = spawn(process.execPath, args, {
2419
3195
  detached: true,
2420
3196
  stdio: ["ignore", logFd, logFd],
2421
3197
  env: process.env
2422
3198
  });
2423
- fs7.closeSync(logFd);
3199
+ fs9.closeSync(logFd);
2424
3200
  child.unref();
2425
3201
  if (!child.pid) {
2426
3202
  throw new Error("Failed to start autonomy daemon: missing child process pid.");
2427
3203
  }
2428
- fs7.writeFileSync(pidPath, `${child.pid}
3204
+ fs9.writeFileSync(pidPath, `${child.pid}
2429
3205
  `, "utf-8");
2430
- fs7.writeFileSync(metaPath, JSON.stringify({
3206
+ fs9.writeFileSync(metaPath, JSON.stringify({
2431
3207
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2432
3208
  pid: child.pid,
2433
3209
  args,
@@ -2461,9 +3237,9 @@ async function stopAutonomyDaemon(workspacePath, input = {}) {
2461
3237
  await waitForProcessExit(pid, 1500);
2462
3238
  stopped = !isProcessAlive(pid);
2463
3239
  }
2464
- const pidPath = path9.join(ensureDaemonDir(workspacePath), AUTONOMY_PID_FILE);
2465
- if (stopped && fs7.existsSync(pidPath)) {
2466
- fs7.rmSync(pidPath, { force: true });
3240
+ const pidPath = path11.join(ensureDaemonDir(workspacePath), AUTONOMY_PID_FILE);
3241
+ if (stopped && fs9.existsSync(pidPath)) {
3242
+ fs9.rmSync(pidPath, { force: true });
2467
3243
  }
2468
3244
  return {
2469
3245
  stopped,
@@ -2474,14 +3250,14 @@ async function stopAutonomyDaemon(workspacePath, input = {}) {
2474
3250
  }
2475
3251
  function readAutonomyDaemonStatus(workspacePath, options = {}) {
2476
3252
  const daemonDir = ensureDaemonDir(workspacePath);
2477
- const pidPath = path9.join(daemonDir, AUTONOMY_PID_FILE);
2478
- const meta = readDaemonMeta(path9.join(daemonDir, AUTONOMY_META_FILE));
2479
- const logPath = meta?.logPath ? String(meta.logPath) : path9.join(daemonDir, AUTONOMY_LOG_FILE);
2480
- const heartbeatPath = meta?.heartbeatPath ? String(meta.heartbeatPath) : path9.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
3253
+ const pidPath = path11.join(daemonDir, AUTONOMY_PID_FILE);
3254
+ const meta = readDaemonMeta(path11.join(daemonDir, AUTONOMY_META_FILE));
3255
+ const logPath = meta?.logPath ? String(meta.logPath) : path11.join(daemonDir, AUTONOMY_LOG_FILE);
3256
+ const heartbeatPath = meta?.heartbeatPath ? String(meta.heartbeatPath) : path11.join(daemonDir, AUTONOMY_HEARTBEAT_FILE);
2481
3257
  const pid = readPid(pidPath);
2482
3258
  const running = pid ? isProcessAlive(pid) : false;
2483
- if (!running && pid && options.cleanupStalePidFile !== false && fs7.existsSync(pidPath)) {
2484
- fs7.rmSync(pidPath, { force: true });
3259
+ if (!running && pid && options.cleanupStalePidFile !== false && fs9.existsSync(pidPath)) {
3260
+ fs9.rmSync(pidPath, { force: true });
2485
3261
  }
2486
3262
  return {
2487
3263
  running,
@@ -2494,7 +3270,7 @@ function readAutonomyDaemonStatus(workspacePath, options = {}) {
2494
3270
  }
2495
3271
  function buildAutonomyDaemonArgs(workspacePath, input, heartbeatPath) {
2496
3272
  const args = [
2497
- path9.resolve(input.cliEntrypointPath),
3273
+ path11.resolve(input.cliEntrypointPath),
2498
3274
  "autonomy",
2499
3275
  "run",
2500
3276
  "-w",
@@ -2552,22 +3328,22 @@ function waitForProcessExit(pid, timeoutMs) {
2552
3328
  });
2553
3329
  }
2554
3330
  function ensureDaemonDir(workspacePath) {
2555
- const daemonDir = path9.join(workspacePath, DAEMON_DIR);
2556
- if (!fs7.existsSync(daemonDir)) fs7.mkdirSync(daemonDir, { recursive: true });
3331
+ const daemonDir = path11.join(workspacePath, DAEMON_DIR);
3332
+ if (!fs9.existsSync(daemonDir)) fs9.mkdirSync(daemonDir, { recursive: true });
2557
3333
  return daemonDir;
2558
3334
  }
2559
3335
  function readPid(pidPath) {
2560
- if (!fs7.existsSync(pidPath)) return void 0;
2561
- const raw = fs7.readFileSync(pidPath, "utf-8").trim();
3336
+ if (!fs9.existsSync(pidPath)) return void 0;
3337
+ const raw = fs9.readFileSync(pidPath, "utf-8").trim();
2562
3338
  if (!raw) return void 0;
2563
3339
  const parsed = Number(raw);
2564
3340
  if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
2565
3341
  return parsed;
2566
3342
  }
2567
3343
  function readHeartbeat(heartbeatPath) {
2568
- if (!fs7.existsSync(heartbeatPath)) return void 0;
3344
+ if (!fs9.existsSync(heartbeatPath)) return void 0;
2569
3345
  try {
2570
- const parsed = JSON.parse(fs7.readFileSync(heartbeatPath, "utf-8"));
3346
+ const parsed = JSON.parse(fs9.readFileSync(heartbeatPath, "utf-8"));
2571
3347
  if (!parsed || typeof parsed !== "object") return void 0;
2572
3348
  return parsed;
2573
3349
  } catch {
@@ -2575,9 +3351,9 @@ function readHeartbeat(heartbeatPath) {
2575
3351
  }
2576
3352
  }
2577
3353
  function readDaemonMeta(metaPath) {
2578
- if (!fs7.existsSync(metaPath)) return void 0;
3354
+ if (!fs9.existsSync(metaPath)) return void 0;
2579
3355
  try {
2580
- const parsed = JSON.parse(fs7.readFileSync(metaPath, "utf-8"));
3356
+ const parsed = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
2581
3357
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
2582
3358
  return parsed;
2583
3359
  } catch {
@@ -2595,9 +3371,9 @@ function isProcessAlive(pid) {
2595
3371
  }
2596
3372
  function isZombieProcess(pid) {
2597
3373
  const statPath = `/proc/${pid}/stat`;
2598
- if (!fs7.existsSync(statPath)) return false;
3374
+ if (!fs9.existsSync(statPath)) return false;
2599
3375
  try {
2600
- const stat = fs7.readFileSync(statPath, "utf-8");
3376
+ const stat = fs9.readFileSync(statPath, "utf-8");
2601
3377
  const closingIdx = stat.indexOf(")");
2602
3378
  if (closingIdx === -1 || closingIdx + 2 >= stat.length) return false;
2603
3379
  const state = stat.slice(closingIdx + 2, closingIdx + 3);
@@ -2606,10 +3382,10 @@ function isZombieProcess(pid) {
2606
3382
  return false;
2607
3383
  }
2608
3384
  }
2609
- function resolvePathWithinWorkspace3(workspacePath, filePath) {
2610
- const base = path9.resolve(workspacePath);
2611
- const resolved = path9.resolve(base, filePath);
2612
- if (!resolved.startsWith(base + path9.sep) && resolved !== base) {
3385
+ function resolvePathWithinWorkspace4(workspacePath, filePath) {
3386
+ const base = path11.resolve(workspacePath);
3387
+ const resolved = path11.resolve(base, filePath);
3388
+ if (!resolved.startsWith(base + path11.sep) && resolved !== base) {
2613
3389
  throw new Error(`Invalid path outside workspace: ${filePath}`);
2614
3390
  }
2615
3391
  return resolved;
@@ -2624,6 +3400,7 @@ export {
2624
3400
  workspace_exports,
2625
3401
  command_center_exports,
2626
3402
  skill_exports,
3403
+ lens_exports,
2627
3404
  board_exports,
2628
3405
  agent_exports,
2629
3406
  onboard_exports,
@@ -2633,6 +3410,7 @@ export {
2633
3410
  fetchSkillMarkdownFromUrl,
2634
3411
  clawdapus_exports,
2635
3412
  integration_exports,
3413
+ swarm_exports,
2636
3414
  diagnostics_exports,
2637
3415
  autonomy_daemon_exports
2638
3416
  };