@useorgx/openclaw-plugin 0.7.18 → 0.7.23

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.
Files changed (161) hide show
  1. package/dashboard/dist/assets/9gFmK3Kr.js +1 -0
  2. package/dashboard/dist/assets/9gFmK3Kr.js.br +0 -0
  3. package/dashboard/dist/assets/9gFmK3Kr.js.gz +0 -0
  4. package/dashboard/dist/assets/{DS79hzMu.js → BrMXbzQ-.js} +2 -2
  5. package/dashboard/dist/assets/BrMXbzQ-.js.br +0 -0
  6. package/dashboard/dist/assets/BrMXbzQ-.js.gz +0 -0
  7. package/dashboard/dist/assets/By0MIBj_.js +1 -0
  8. package/dashboard/dist/assets/By0MIBj_.js.br +0 -0
  9. package/dashboard/dist/assets/By0MIBj_.js.gz +0 -0
  10. package/dashboard/dist/assets/C1u2SGin.css +1 -0
  11. package/dashboard/dist/assets/C1u2SGin.css.br +0 -0
  12. package/dashboard/dist/assets/C1u2SGin.css.gz +0 -0
  13. package/dashboard/dist/assets/{467jKHFJ.js → CGJiHCIx.js} +1 -1
  14. package/dashboard/dist/assets/CGJiHCIx.js.br +0 -0
  15. package/dashboard/dist/assets/CGJiHCIx.js.gz +0 -0
  16. package/dashboard/dist/assets/CSd4rSuU.js +212 -0
  17. package/dashboard/dist/assets/CSd4rSuU.js.br +0 -0
  18. package/dashboard/dist/assets/CSd4rSuU.js.gz +0 -0
  19. package/dashboard/dist/assets/{5Ihga-4X.js → CZXS5i_5.js} +1 -1
  20. package/dashboard/dist/assets/CZXS5i_5.js.br +0 -0
  21. package/dashboard/dist/assets/CZXS5i_5.js.gz +0 -0
  22. package/dashboard/dist/assets/{a6qcPiWt.js → CbVWL74-.js} +1 -1
  23. package/dashboard/dist/assets/CbVWL74-.js.br +0 -0
  24. package/dashboard/dist/assets/CbVWL74-.js.gz +0 -0
  25. package/dashboard/dist/assets/{qDJ6rqcs.js → D-FuHfT8.js} +1 -1
  26. package/dashboard/dist/assets/D-FuHfT8.js.br +0 -0
  27. package/dashboard/dist/assets/D-FuHfT8.js.gz +0 -0
  28. package/dashboard/dist/assets/{BcJmNILk.js → D0PN5_vY.js} +1 -1
  29. package/dashboard/dist/assets/D0PN5_vY.js.br +0 -0
  30. package/dashboard/dist/assets/D0PN5_vY.js.gz +0 -0
  31. package/dashboard/dist/assets/DDCPrZRt.js +1 -0
  32. package/dashboard/dist/assets/DDCPrZRt.js.br +0 -0
  33. package/dashboard/dist/assets/DDCPrZRt.js.gz +0 -0
  34. package/dashboard/dist/assets/{B71dt9yu.js → DNQ-iFO2.js} +1 -1
  35. package/dashboard/dist/assets/DNQ-iFO2.js.br +0 -0
  36. package/dashboard/dist/assets/DNQ-iFO2.js.gz +0 -0
  37. package/dashboard/dist/assets/{PVi0vr9a.js → DhPuHPK7.js} +1 -1
  38. package/dashboard/dist/assets/DhPuHPK7.js.br +0 -0
  39. package/dashboard/dist/assets/DhPuHPK7.js.gz +0 -0
  40. package/dashboard/dist/assets/Dhz7qPtn.js +1 -0
  41. package/dashboard/dist/assets/Dhz7qPtn.js.br +0 -0
  42. package/dashboard/dist/assets/Dhz7qPtn.js.gz +0 -0
  43. package/dashboard/dist/assets/LOFrVoPD.js +1 -0
  44. package/dashboard/dist/assets/LOFrVoPD.js.br +0 -0
  45. package/dashboard/dist/assets/LOFrVoPD.js.gz +0 -0
  46. package/dashboard/dist/assets/OlLPtzdz.js +1 -0
  47. package/dashboard/dist/assets/OlLPtzdz.js.br +0 -0
  48. package/dashboard/dist/assets/OlLPtzdz.js.gz +0 -0
  49. package/dashboard/dist/assets/{sdoPH_Z1.js → RN4M9u9W.js} +2 -2
  50. package/dashboard/dist/assets/RN4M9u9W.js.br +0 -0
  51. package/dashboard/dist/assets/RN4M9u9W.js.gz +0 -0
  52. package/dashboard/dist/assets/VCHu272d.js +1 -0
  53. package/dashboard/dist/assets/VCHu272d.js.br +0 -0
  54. package/dashboard/dist/assets/VCHu272d.js.gz +0 -0
  55. package/dashboard/dist/assets/m2smti3F.js +1 -0
  56. package/dashboard/dist/assets/m2smti3F.js.br +0 -0
  57. package/dashboard/dist/assets/m2smti3F.js.gz +0 -0
  58. package/dashboard/dist/assets/{C3_j_W9V.js → nra1yvJX.js} +1 -1
  59. package/dashboard/dist/assets/nra1yvJX.js.br +0 -0
  60. package/dashboard/dist/assets/nra1yvJX.js.gz +0 -0
  61. package/dashboard/dist/assets/qLX6NZ-J.js +1 -0
  62. package/dashboard/dist/assets/qLX6NZ-J.js.br +0 -0
  63. package/dashboard/dist/assets/qLX6NZ-J.js.gz +0 -0
  64. package/dashboard/dist/index.html +2 -2
  65. package/dashboard/dist/index.html.br +0 -0
  66. package/dashboard/dist/index.html.gz +0 -0
  67. package/dist/agent-run-store.js +162 -24
  68. package/dist/cli/orgx.d.ts +3 -0
  69. package/dist/config/resolution.d.ts +7 -0
  70. package/dist/config/resolution.js +13 -5
  71. package/dist/contracts/onboarding-state.d.ts +2 -0
  72. package/dist/contracts/onboarding-state.js +23 -0
  73. package/dist/contracts/shared-types.d.ts +17 -0
  74. package/dist/http/helpers/auto-continue-engine.d.ts +62 -0
  75. package/dist/http/helpers/auto-continue-engine.js +329 -53
  76. package/dist/http/helpers/autopilot-runtime.js +5 -1
  77. package/dist/http/helpers/autopilot-slice-utils.js +25 -1
  78. package/dist/http/helpers/decision-mapper.d.ts +1 -0
  79. package/dist/http/helpers/decision-mapper.js +19 -2
  80. package/dist/http/helpers/dispatch-lifecycle.js +3 -0
  81. package/dist/http/helpers/mission-control.d.ts +1 -0
  82. package/dist/http/helpers/mission-control.js +5 -2
  83. package/dist/http/helpers/slice-run-projections.d.ts +27 -0
  84. package/dist/http/helpers/slice-run-projections.js +198 -10
  85. package/dist/http/helpers/triage-mapper.js +220 -6
  86. package/dist/http/index.d.ts +1 -0
  87. package/dist/http/index.js +94 -46
  88. package/dist/http/router.js +64 -9
  89. package/dist/http/routes/live-legacy.d.ts +19 -2
  90. package/dist/http/routes/live-legacy.js +110 -27
  91. package/dist/http/routes/live-snapshot.d.ts +16 -2
  92. package/dist/http/routes/live-snapshot.js +169 -25
  93. package/dist/http/routes/mission-control-actions.js +28 -0
  94. package/dist/http/routes/mission-control-read.d.ts +18 -0
  95. package/dist/http/routes/mission-control-read.js +130 -218
  96. package/dist/http/routes/onboarding.d.ts +1 -0
  97. package/dist/http/routes/onboarding.js +17 -0
  98. package/dist/index.d.ts +5 -0
  99. package/dist/index.js +199 -123
  100. package/dist/outbox.d.ts +0 -2
  101. package/dist/outbox.js +268 -150
  102. package/dist/reporting/rollups.js +18 -11
  103. package/dist/runtime-instance-store.js +212 -58
  104. package/dist/stores/materialized-snapshot-store.d.ts +18 -0
  105. package/dist/stores/materialized-snapshot-store.js +91 -0
  106. package/dist/stores/sqlite-state.d.ts +6 -0
  107. package/dist/stores/sqlite-state.js +179 -0
  108. package/package.json +6 -1
  109. package/dashboard/dist/assets/467jKHFJ.js.br +0 -0
  110. package/dashboard/dist/assets/467jKHFJ.js.gz +0 -0
  111. package/dashboard/dist/assets/5Ihga-4X.js.br +0 -0
  112. package/dashboard/dist/assets/5Ihga-4X.js.gz +0 -0
  113. package/dashboard/dist/assets/B71dt9yu.js.br +0 -0
  114. package/dashboard/dist/assets/B71dt9yu.js.gz +0 -0
  115. package/dashboard/dist/assets/BCudUvwg.js +0 -1
  116. package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
  117. package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
  118. package/dashboard/dist/assets/BEnI6kNR.js +0 -1
  119. package/dashboard/dist/assets/BEnI6kNR.js.br +0 -0
  120. package/dashboard/dist/assets/BEnI6kNR.js.gz +0 -0
  121. package/dashboard/dist/assets/BcJmNILk.js.br +0 -0
  122. package/dashboard/dist/assets/BcJmNILk.js.gz +0 -0
  123. package/dashboard/dist/assets/C-MOJWHs.js +0 -1
  124. package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
  125. package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
  126. package/dashboard/dist/assets/C-XuWXGi.js +0 -1
  127. package/dashboard/dist/assets/C-XuWXGi.js.br +0 -0
  128. package/dashboard/dist/assets/C-XuWXGi.js.gz +0 -0
  129. package/dashboard/dist/assets/C3_j_W9V.js.br +0 -0
  130. package/dashboard/dist/assets/C3_j_W9V.js.gz +0 -0
  131. package/dashboard/dist/assets/C9-UYhBb.js +0 -1
  132. package/dashboard/dist/assets/C9-UYhBb.js.br +0 -0
  133. package/dashboard/dist/assets/C9-UYhBb.js.gz +0 -0
  134. package/dashboard/dist/assets/C9yV06GS.js +0 -1
  135. package/dashboard/dist/assets/C9yV06GS.js.br +0 -0
  136. package/dashboard/dist/assets/C9yV06GS.js.gz +0 -0
  137. package/dashboard/dist/assets/CReugbyT.js +0 -1
  138. package/dashboard/dist/assets/CReugbyT.js.br +0 -0
  139. package/dashboard/dist/assets/CReugbyT.js.gz +0 -0
  140. package/dashboard/dist/assets/CSDhTbKy.js +0 -1
  141. package/dashboard/dist/assets/CSDhTbKy.js.br +0 -0
  142. package/dashboard/dist/assets/CSDhTbKy.js.gz +0 -0
  143. package/dashboard/dist/assets/CfMS9yIf.js +0 -1
  144. package/dashboard/dist/assets/CfMS9yIf.js.br +0 -0
  145. package/dashboard/dist/assets/CfMS9yIf.js.gz +0 -0
  146. package/dashboard/dist/assets/D2Kqcmv9.js +0 -212
  147. package/dashboard/dist/assets/D2Kqcmv9.js.br +0 -0
  148. package/dashboard/dist/assets/D2Kqcmv9.js.gz +0 -0
  149. package/dashboard/dist/assets/DS79hzMu.js.br +0 -0
  150. package/dashboard/dist/assets/DS79hzMu.js.gz +0 -0
  151. package/dashboard/dist/assets/PVi0vr9a.js.br +0 -0
  152. package/dashboard/dist/assets/PVi0vr9a.js.gz +0 -0
  153. package/dashboard/dist/assets/RZkbqlJk.css +0 -1
  154. package/dashboard/dist/assets/RZkbqlJk.css.br +0 -0
  155. package/dashboard/dist/assets/RZkbqlJk.css.gz +0 -0
  156. package/dashboard/dist/assets/a6qcPiWt.js.br +0 -0
  157. package/dashboard/dist/assets/a6qcPiWt.js.gz +0 -0
  158. package/dashboard/dist/assets/qDJ6rqcs.js.br +0 -0
  159. package/dashboard/dist/assets/qDJ6rqcs.js.gz +0 -0
  160. package/dashboard/dist/assets/sdoPH_Z1.js.br +0 -0
  161. package/dashboard/dist/assets/sdoPH_Z1.js.gz +0 -0
@@ -1,5 +1,6 @@
1
1
  import { listBuiltInSentinels } from "../helpers/sentinel-catalog.js";
2
2
  import { resolveWorkspaceScope, workspaceScopeFromHeaders, } from "../helpers/workspace-scope.js";
3
+ import { isDoneStatus, normalizeMissionControlStatus, } from "../helpers/mission-control.js";
3
4
  import { asRecord, asString, asNumber, asArray, asStringArray, } from "../../lib/type-coercion.js";
4
5
  // asRecord, asString, asArray, asNumber, asStringArray imported from ../../lib/type-coercion.js
5
6
  function normalizeRunnerValue(value) {
@@ -313,7 +314,7 @@ async function requestCanonicalWithLegacyFallback(deps, input) {
313
314
  return await withSoftTimeout(deps.rawRequest("GET", input.legacyPath), Math.min(input.timeoutMs, 1_500), `${input.label} (legacy fallback)`);
314
315
  }
315
316
  function normalizeQueueState(value) {
316
- const normalized = normalizeStatus(asString(value));
317
+ const normalized = normalizeMissionControlStatus(asString(value));
317
318
  if (normalized === "running" || normalized === "in_progress") {
318
319
  return "running";
319
320
  }
@@ -336,19 +337,6 @@ function normalizeQueueState(value) {
336
337
  return "completed";
337
338
  return "idle";
338
339
  }
339
- function normalizeStatus(value) {
340
- return (value ?? "").trim().toLowerCase().replace(/[\s-]+/g, "_");
341
- }
342
- function isDoneStatus(value) {
343
- const normalized = normalizeStatus(value);
344
- return (normalized === "done" ||
345
- normalized === "completed" ||
346
- normalized === "resolved" ||
347
- normalized === "cancelled" ||
348
- normalized === "canceled" ||
349
- normalized === "archived" ||
350
- normalized === "closed");
351
- }
352
340
  function queueStateRank(state) {
353
341
  if (state === "running")
354
342
  return 0;
@@ -476,7 +464,7 @@ function normalizeQueueItems(input) {
476
464
  ? runnerSourceHint ?? "inferred"
477
465
  : "fallback";
478
466
  const queueState = normalizeQueueState(record.queueState ?? record.queue_state);
479
- const normalizedSliceScope = normalizeStatus(asString(record.sliceScope) ?? asString(record.slice_scope));
467
+ const normalizedSliceScope = normalizeMissionControlStatus(asString(record.sliceScope) ?? asString(record.slice_scope));
480
468
  const sliceScope = normalizedSliceScope === "task" ||
481
469
  normalizedSliceScope === "milestone" ||
482
470
  normalizedSliceScope === "workstream"
@@ -561,6 +549,34 @@ function normalizeQueueItems(input) {
561
549
  return left.workstreamTitle.localeCompare(right.workstreamTitle);
562
550
  });
563
551
  }
552
+ function summarizeQueueItems(items) {
553
+ const stateCounts = {
554
+ queued: 0,
555
+ running: 0,
556
+ blocked: 0,
557
+ idle: 0,
558
+ completed: 0,
559
+ };
560
+ for (const item of items) {
561
+ stateCounts[item.queueState] += 1;
562
+ }
563
+ return {
564
+ visibleTotal: items.filter((item) => item.queueState !== "running" && item.queueState !== "completed").length,
565
+ stateCounts,
566
+ };
567
+ }
568
+ function dedupeQueueItemsByLineage(items) {
569
+ const seen = new Set();
570
+ const deduped = [];
571
+ for (const item of items) {
572
+ const key = `${item.initiativeId}:${item.workstreamId}`;
573
+ if (seen.has(key))
574
+ continue;
575
+ seen.add(key);
576
+ deduped.push(item);
577
+ }
578
+ return deduped;
579
+ }
564
580
  function isHighSeverityQueueItem(item) {
565
581
  if (item.scoringTier === "urgent")
566
582
  return true;
@@ -613,135 +629,13 @@ function applyQueueNoiseControls(items, options) {
613
629
  }
614
630
  return deduped;
615
631
  }
616
- function mapCanonicalSlicesToQueueItems(input) {
617
- const queueLike = [];
618
- for (const entry of input) {
619
- const record = asRecord(entry);
620
- if (!record)
621
- continue;
622
- const initiativeId = asString(record.initiativeId) ?? asString(record.initiative_id);
623
- const workstreamId = asString(record.workstreamId) ?? asString(record.workstream_id);
624
- if (!initiativeId || !workstreamId)
625
- continue;
626
- const dispatch = asRecord(record.dispatch) ?? {};
627
- const lineage = asRecord(record.lineage) ?? {};
628
- const taskId = asString(record.taskId) ?? asString(record.task_id);
629
- const sliceTaskIds = dedupeStrings([
630
- ...asStringArray(record.sliceTaskIds),
631
- ...asStringArray(record.slice_task_ids),
632
- ...asStringArray(lineage.taskIds),
633
- ...asStringArray(lineage.task_ids),
634
- ...(taskId ? [taskId] : []),
635
- ]);
636
- const rawStatus = asString(record.status) ?? "active";
637
- const normalizedStatus = normalizeStatus(rawStatus);
638
- const runnable = Boolean(dispatch.runnable);
639
- let queueState;
640
- if (isDoneStatus(rawStatus)) {
641
- queueState = "completed";
642
- }
643
- else if (normalizedStatus === "running" || normalizedStatus === "in_progress") {
644
- queueState = "running";
645
- }
646
- else if (normalizedStatus === "blocked" ||
647
- normalizedStatus === "waiting_dependency" ||
648
- normalizedStatus === "needs_decision" ||
649
- normalizedStatus === "waiting_on_decision" ||
650
- normalizedStatus === "paused" ||
651
- !runnable) {
652
- queueState = "blocked";
653
- }
654
- else if (normalizedStatus === "idle" ||
655
- normalizedStatus === "not_started" ||
656
- normalizedStatus === "draft") {
657
- queueState = "idle";
658
- }
659
- else {
660
- queueState = "queued";
661
- }
662
- const runnerAgentIdRaw = normalizeRunnerValue(record.runnerAgentId) ?? normalizeRunnerValue(record.runner_agent_id);
663
- const runnerAgentNameRaw = normalizeRunnerValue(record.runnerAgentName) ?? normalizeRunnerValue(record.runner_agent_name);
664
- const runnerAgents = mergeRunnerAgents(normalizeRunnerAgents(record.runnerAgents), normalizeRunnerAgents(record.runner_agents), runnerAgentIdRaw || runnerAgentNameRaw
665
- ? [
666
- {
667
- id: runnerAgentIdRaw ?? runnerAgentNameRaw ?? "Unassigned",
668
- name: runnerAgentNameRaw ?? runnerAgentIdRaw ?? "Unassigned",
669
- },
670
- ]
671
- : []);
672
- const runnerSourceHint = normalizeRunnerSource(record.runnerSource) ?? normalizeRunnerSource(record.runner_source);
673
- const runnerSource = runnerAgents.length > 0 ? runnerSourceHint ?? "inferred" : "fallback";
674
- const suggestedScope = normalizeStatus(asString(dispatch.suggestedScope) ??
675
- asString(dispatch.suggested_scope) ??
676
- asString(record.level));
677
- const sliceScope = suggestedScope === "task" ||
678
- suggestedScope === "milestone" ||
679
- suggestedScope === "workstream"
680
- ? suggestedScope
681
- : null;
682
- const order = asRecord(record.order) ?? {};
683
- const manualRank = asNumber(order.manualRank ?? order.manual_rank);
684
- const iwmt = asRecord(record.iwmt);
685
- const objective = asRecord(record.objective);
686
- queueLike.push({
687
- initiativeId,
688
- initiativeTitle: asString(record.initiativeTitle) ??
689
- asString(record.initiative_title) ??
690
- initiativeId,
691
- initiativeStatus: asString(record.initiativeStatus) ??
692
- asString(record.initiative_status) ??
693
- "active",
694
- initiativePriority: asString(record.initiativePriority) ?? asString(record.initiative_priority),
695
- initiativePriorityNum: asNumber(record.initiativePriorityNum ?? record.initiative_priority_num),
696
- workstreamId,
697
- workstreamTitle: asString(record.workstreamTitle) ??
698
- asString(record.workstream_title) ??
699
- asString(record.title) ??
700
- workstreamId,
701
- workstreamStatus: asString(record.workstreamStatus) ??
702
- asString(record.workstream_status) ??
703
- rawStatus,
704
- nextTaskId: taskId ?? sliceTaskIds[0] ?? null,
705
- nextTaskTitle: asString(record.nextTaskTitle) ?? asString(record.next_task_title),
706
- nextTaskPriority: asNumber(record.priorityNum ??
707
- record.priority_num ??
708
- record.nextTaskPriority ??
709
- record.next_task_priority),
710
- nextTaskDueAt: asString(record.dueAt) ??
711
- asString(record.due_at) ??
712
- asString(record.nextTaskDueAt) ??
713
- asString(record.next_task_due_at),
714
- nextTaskMilestoneId: asString(record.milestoneId) ?? asString(record.milestone_id),
715
- runnerAgentId: runnerAgentIdRaw,
716
- runnerAgentName: runnerAgentNameRaw,
717
- runnerAgents,
718
- runnerSource,
719
- queueState,
720
- blockReason: asString(dispatch.blockReason) ??
721
- asString(dispatch.block_reason) ??
722
- null,
723
- sliceScope,
724
- sliceTaskIds,
725
- sliceTaskCount: sliceTaskIds.length,
726
- sliceMilestoneId: asString(record.milestoneId) ?? asString(record.milestone_id),
727
- isPinned: typeof manualRank === "number",
728
- pinnedRank: manualRank,
729
- compositeScore: asNumber(iwmt?.mixScore ?? objective?.objectiveScore),
730
- objectiveScore: asNumber(objective?.objectiveScore) ?? undefined,
731
- roiPerToken: asNumber(iwmt?.roiPerToken) ?? undefined,
732
- expectedTokens: asNumber(iwmt?.expectedTokens) ?? undefined,
733
- expectedValueUsd: asNumber(iwmt?.expectedValueUsd) ?? undefined,
734
- updatedAt: asString(record.updatedAt) ?? asString(record.updated_at) ?? null,
735
- });
736
- }
737
- return normalizeQueueItems(queueLike);
738
- }
739
632
  async function loadInitiativeGraphIndex(deps, initiativeId) {
740
633
  const graphRaw = deps.applyLocalInitiativeOverrideToGraph(await deps.buildMissionControlGraph(initiativeId));
741
634
  const graph = asRecord(graphRaw);
742
635
  const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
743
636
  const tasksById = new Map();
744
637
  const milestoneTitleById = new Map();
638
+ const milestoneWorkstream = new Map();
745
639
  for (const nodeEntry of nodes) {
746
640
  const node = asRecord(nodeEntry);
747
641
  if (!node)
@@ -752,6 +646,9 @@ async function loadInitiativeGraphIndex(deps, initiativeId) {
752
646
  continue;
753
647
  if (type === "milestone") {
754
648
  milestoneTitleById.set(id, asString(node.title) ?? id);
649
+ const wsId = asString(node.workstreamId) ?? asString(node.parentId);
650
+ if (wsId)
651
+ milestoneWorkstream.set(id, wsId);
755
652
  continue;
756
653
  }
757
654
  if (type !== "task")
@@ -767,11 +664,63 @@ async function loadInitiativeGraphIndex(deps, initiativeId) {
767
664
  updatedAt: asString(node.updatedAt),
768
665
  });
769
666
  }
667
+ // Build milestonesByWorkstream from milestones + their child tasks
668
+ const milestonesByWorkstream = new Map();
669
+ for (const [msId, wsId] of milestoneWorkstream) {
670
+ const taskIds = [];
671
+ for (const task of tasksById.values()) {
672
+ if (task.milestoneId === msId)
673
+ taskIds.push(task.id);
674
+ }
675
+ const entry = { id: msId, title: milestoneTitleById.get(msId) ?? msId, taskIds };
676
+ const existing = milestonesByWorkstream.get(wsId) ?? [];
677
+ existing.push(entry);
678
+ milestonesByWorkstream.set(wsId, existing);
679
+ }
770
680
  return {
771
681
  tasksById,
772
682
  milestoneTitleById,
683
+ milestonesByWorkstream,
773
684
  };
774
685
  }
686
+ async function enrichWithMilestoneBreakdown(items, deps) {
687
+ if (items.length === 0)
688
+ return items;
689
+ const graphByInitiative = new Map();
690
+ const uniqueInitiatives = dedupeStrings(items.map((i) => i.initiativeId));
691
+ for (const id of uniqueInitiatives) {
692
+ try {
693
+ graphByInitiative.set(id, await loadInitiativeGraphIndex(deps, id));
694
+ }
695
+ catch {
696
+ // graph unavailable — skip enrichment for this initiative
697
+ }
698
+ }
699
+ if (graphByInitiative.size === 0)
700
+ return items;
701
+ for (const item of items) {
702
+ const graph = graphByInitiative.get(item.initiativeId);
703
+ if (!graph)
704
+ continue;
705
+ const milestones = graph.milestonesByWorkstream.get(item.workstreamId) ?? [];
706
+ if (milestones.length === 0)
707
+ continue;
708
+ item.milestoneBreakdown = milestones.map((ms) => {
709
+ const tasks = ms.taskIds.map((tid) => {
710
+ const task = graph.tasksById.get(tid);
711
+ return { id: tid, title: task?.title ?? "Untitled", status: task?.status ?? "pending" };
712
+ });
713
+ return {
714
+ id: ms.id,
715
+ title: ms.title,
716
+ tasks,
717
+ totalTasks: tasks.length,
718
+ doneTasks: tasks.filter((t) => t.status === "done" || t.status === "completed").length,
719
+ };
720
+ });
721
+ }
722
+ return items;
723
+ }
775
724
  export function registerMissionControlReadRoutes(router, deps) {
776
725
  // Handler registrations are process-local. Reset route caches so each newly
777
726
  // constructed handler starts from a clean canonical cache/bypass state.
@@ -803,7 +752,10 @@ export function registerMissionControlReadRoutes(router, deps) {
803
752
  sendRouteError(res, 400, "mission-control.read.auto-continue.status.validation", "initiativeId is required");
804
753
  return;
805
754
  }
806
- const run = deps.autoContinueRuns.get(id) ?? null;
755
+ let run = deps.autoContinueRuns.get(id) ?? null;
756
+ if (!run && deps.restoreAutoContinueRun) {
757
+ run = await deps.restoreAutoContinueRun(id) ?? null;
758
+ }
807
759
  deps.sendJson(res, 200, {
808
760
  ok: true,
809
761
  initiativeId: id,
@@ -953,13 +905,42 @@ export function registerMissionControlReadRoutes(router, deps) {
953
905
  if (isCanonicalAllScopeMismatch(canonicalRecord, useAllScope)) {
954
906
  throw new Error("canonical next-up all-workspaces scope mismatch");
955
907
  }
956
- const canonicalItems = applyQueueNoiseControls(normalizeQueueItems(canonicalRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
908
+ const canonicalItems = await enrichWithMilestoneBreakdown(applyQueueNoiseControls(normalizeQueueItems(canonicalRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs }), deps);
957
909
  const canonicalTotal = Math.max(canonicalItems.length, Math.floor(asNumber(canonicalRecord.total) ?? canonicalItems.length)) ?? canonicalItems.length;
958
910
  const canonicalPagination = parsePaginationEnvelope(canonicalRecord.pagination, {
959
911
  offset,
960
912
  limit: pageSize,
961
913
  total: canonicalTotal,
962
914
  });
915
+ let canonicalSummaryItems = canonicalItems;
916
+ const summaryPageSize = Math.max(pageSize, Math.min(canonicalTotal, 200));
917
+ if (canonicalTotal > canonicalItems.length) {
918
+ const summaryItems = [];
919
+ for (let summaryOffset = 0; summaryOffset < canonicalTotal; summaryOffset += summaryPageSize) {
920
+ const summaryParams = new URLSearchParams(params);
921
+ summaryParams.set("offset", String(summaryOffset));
922
+ summaryParams.set("limit", String(summaryPageSize));
923
+ const summaryPage = await requestCanonicalWithLegacyFallback(deps, {
924
+ timeoutMs: CANONICAL_NEXT_UP_TIMEOUT_MS,
925
+ label: "canonical next-up summary",
926
+ modernPath: `/api/client/mission-control/next-up?${summaryParams.toString()}`,
927
+ legacyPath: `/api/mission-control/next-up?${summaryParams.toString()}`,
928
+ });
929
+ const summaryRecord = asRecord(summaryPage);
930
+ if (!summaryRecord || !Array.isArray(summaryRecord.items)) {
931
+ throw new Error("invalid canonical next-up summary payload");
932
+ }
933
+ const normalizedSummaryItems = applyQueueNoiseControls(normalizeQueueItems(summaryRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
934
+ summaryItems.push(...normalizedSummaryItems);
935
+ if (normalizedSummaryItems.length < summaryPageSize) {
936
+ break;
937
+ }
938
+ }
939
+ canonicalSummaryItems =
940
+ summaryItems.length > 0
941
+ ? dedupeQueueItemsByLineage(summaryItems)
942
+ : canonicalItems;
943
+ }
963
944
  const shouldRepaginateCanonically = canonicalItems.length > pageSize ||
964
945
  canonicalPagination.offset !== offset ||
965
946
  canonicalPagination.limit !== pageSize;
@@ -972,11 +953,13 @@ export function registerMissionControlReadRoutes(router, deps) {
972
953
  })
973
954
  : null;
974
955
  const degraded = dedupeStrings(asStringArray(canonicalRecord.degraded));
956
+ const canonicalSummary = summarizeQueueItems(canonicalSummaryItems);
975
957
  const responsePayload = {
976
958
  ok: true,
977
959
  generatedAt: asString(canonicalRecord.generatedAt) ?? new Date().toISOString(),
978
960
  total: paged ? paged.filtered.length : canonicalPagination.total,
979
961
  items: paged ? paged.paged : canonicalItems,
962
+ summary: canonicalSummary,
980
963
  pagination: paged ? paged.pagination : canonicalPagination,
981
964
  source: "canonical",
982
965
  degraded,
@@ -1045,86 +1028,13 @@ export function registerMissionControlReadRoutes(router, deps) {
1045
1028
  return;
1046
1029
  }
1047
1030
  canonicalFallbackReason = `canonical next-up unavailable (${deps.safeErrorMessage(err)})`;
1048
- if (projectId || useAllScope) {
1049
- try {
1050
- const bridgeParams = new URLSearchParams();
1051
- if (initiativeId)
1052
- bridgeParams.set("initiative_id", initiativeId);
1053
- if (projectId) {
1054
- bridgeParams.set("workspace_id", projectId);
1055
- bridgeParams.set("command_center_id", projectId);
1056
- }
1057
- else if (useAllScope) {
1058
- bridgeParams.set("workspace_id", "all");
1059
- bridgeParams.set("command_center_id", "all");
1060
- }
1061
- bridgeParams.set("level", "workstream");
1062
- bridgeParams.set("offset", String(Math.max(0, offset)));
1063
- bridgeParams.set("limit", String(Math.min(300, Math.max(pageSize, offset + pageSize))));
1064
- bridgeParams.set("include_completed", includeCompleted ? "1" : "0");
1065
- bridgeParams.set("noise_threshold", noiseThreshold);
1066
- bridgeParams.set("dedup_window", String(dedupWindowMs));
1067
- bridgeParams.set("mix_policy", requestedMixPolicy ?? "iwmt_v1");
1068
- if (requestedOrderMode) {
1069
- bridgeParams.set("order_mode", requestedOrderMode);
1070
- }
1071
- const canonicalSlices = await requestCanonicalWithLegacyFallback(deps, {
1072
- timeoutMs: CANONICAL_SLICES_TIMEOUT_MS,
1073
- label: "canonical slices bridge",
1074
- modernPath: `/api/client/mission-control/slices?${bridgeParams.toString()}`,
1075
- legacyPath: `/api/mission-control/slices?${bridgeParams.toString()}`,
1076
- });
1077
- const canonicalSlicesRecord = asRecord(canonicalSlices);
1078
- if (!canonicalSlicesRecord || !Array.isArray(canonicalSlicesRecord.items)) {
1079
- throw new Error("invalid canonical slices payload");
1080
- }
1081
- if (isCanonicalAllScopeMismatch(canonicalSlicesRecord, useAllScope)) {
1082
- throw new Error("canonical slices all-workspaces scope mismatch");
1083
- }
1084
- const bridgedItems = applyQueueNoiseControls(mapCanonicalSlicesToQueueItems(canonicalSlicesRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
1085
- if (bridgedItems.length > 0) {
1086
- const paged = applySliceSearchAndPagination({
1087
- items: bridgedItems,
1088
- searchTerm: "",
1089
- offset,
1090
- limit: pageSize,
1091
- });
1092
- const degraded = dedupeStrings([
1093
- ...(Array.isArray(canonicalSlicesRecord.degraded)
1094
- ? canonicalSlicesRecord.degraded
1095
- : []),
1096
- ...(canonicalFallbackReason ? [canonicalFallbackReason] : []),
1097
- "Next Up derived from canonical slices.",
1098
- ]);
1099
- const responsePayload = {
1100
- ok: true,
1101
- generatedAt: asString(canonicalSlicesRecord.generatedAt) ??
1102
- new Date().toISOString(),
1103
- total: paged.filtered.length,
1104
- items: paged.paged,
1105
- pagination: paged.pagination,
1106
- source: "canonical_slices_bridge",
1107
- degraded,
1108
- };
1109
- writeCanonicalReadCache(nextUpCanonicalCacheKey, responsePayload);
1110
- deps.sendJson(res, 200, responsePayload);
1111
- return;
1112
- }
1113
- }
1114
- catch (bridgeErr) {
1115
- canonicalFallbackReason = dedupeStrings([
1116
- canonicalFallbackReason ?? "",
1117
- `canonical slices bridge unavailable (${deps.safeErrorMessage(bridgeErr)})`,
1118
- ]).join(" | ");
1119
- }
1120
- }
1121
1031
  // Continue to local fallback.
1122
1032
  try {
1123
1033
  const queue = await deps.buildNextUpQueue({
1124
1034
  initiativeId,
1125
1035
  projectId,
1126
1036
  });
1127
- const items = applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
1037
+ const items = await enrichWithMilestoneBreakdown(applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs }), deps);
1128
1038
  const paged = applySliceSearchAndPagination({
1129
1039
  items,
1130
1040
  searchTerm: "",
@@ -1140,6 +1050,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1140
1050
  generatedAt: new Date().toISOString(),
1141
1051
  total: paged.filtered.length,
1142
1052
  items: paged.paged,
1053
+ summary: summarizeQueueItems(items),
1143
1054
  pagination: paged.pagination,
1144
1055
  source: "local_fallback",
1145
1056
  degraded,
@@ -1157,7 +1068,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1157
1068
  initiativeId,
1158
1069
  projectId,
1159
1070
  });
1160
- const items = applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
1071
+ const items = await enrichWithMilestoneBreakdown(applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs }), deps);
1161
1072
  const paged = applySliceSearchAndPagination({
1162
1073
  items,
1163
1074
  searchTerm: "",
@@ -1173,6 +1084,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1173
1084
  generatedAt: new Date().toISOString(),
1174
1085
  total: paged.filtered.length,
1175
1086
  items: paged.paged,
1087
+ summary: summarizeQueueItems(items),
1176
1088
  pagination: paged.pagination,
1177
1089
  source: "local",
1178
1090
  degraded: dedupeStrings(degraded),
@@ -1544,7 +1456,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1544
1456
  const taskBuckets = new Map();
1545
1457
  for (const taskId of selectedTaskIds) {
1546
1458
  const task = graphIndex?.tasksById.get(taskId) ?? null;
1547
- if (!includeCompleted && isDoneStatus(task?.status ?? null))
1459
+ if (!includeCompleted && isDoneStatus(task?.status ?? ""))
1548
1460
  continue;
1549
1461
  const milestoneId = task?.milestoneId ??
1550
1462
  item.sliceMilestoneId ??
@@ -1623,7 +1535,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1623
1535
  ]);
1624
1536
  for (const taskId of selectedTaskIds) {
1625
1537
  const task = graphIndex?.tasksById.get(taskId) ?? null;
1626
- if (!includeCompleted && isDoneStatus(task?.status ?? null))
1538
+ if (!includeCompleted && isDoneStatus(task?.status ?? ""))
1627
1539
  continue;
1628
1540
  const taskTitle = task?.title ??
1629
1541
  (taskId === item.nextTaskId ? item.nextTaskTitle : null) ??
@@ -1644,7 +1556,7 @@ export function registerMissionControlReadRoutes(router, deps) {
1644
1556
  : null,
1645
1557
  taskId,
1646
1558
  taskTitle,
1647
- queueState: isDoneStatus(task?.status ?? null) ? "completed" : item.queueState,
1559
+ queueState: isDoneStatus(task?.status ?? "") ? "completed" : item.queueState,
1648
1560
  sourceWorkstreamIds: [item.workstreamId],
1649
1561
  runnerAgentId: (item.runnerAgents ?? [])[0]?.id ?? item.runnerAgentId ?? null,
1650
1562
  runnerAgentName: (item.runnerAgents ?? [])[0]?.name ?? item.runnerAgentName ?? "Unassigned",
@@ -18,6 +18,7 @@ type OnboardingControllerLike = {
18
18
  apiKey: string;
19
19
  userId?: string;
20
20
  }) => Promise<OnboardingState>;
21
+ cancelPairing?: () => Promise<OnboardingState>;
21
22
  disconnect: () => Promise<OnboardingState>;
22
23
  };
23
24
  type RegisterOnboardingRoutesDeps<TReq, TRes> = {
@@ -83,6 +83,23 @@ export function registerOnboardingRoutes(router, deps) {
83
83
  });
84
84
  }
85
85
  }, "Submit manual OrgX API key");
86
+ router.add("POST", "onboarding/cancel", async ({ res }) => {
87
+ try {
88
+ const state = deps.onboarding.cancelPairing
89
+ ? await deps.onboarding.cancelPairing()
90
+ : await deps.onboarding.getStatus();
91
+ deps.sendJson(res, 200, {
92
+ ok: true,
93
+ data: deps.getOnboardingState(state),
94
+ });
95
+ }
96
+ catch (err) {
97
+ deps.sendJson(res, 500, {
98
+ ok: false,
99
+ error: deps.safeErrorMessage(err),
100
+ });
101
+ }
102
+ }, "Cancel onboarding pairing flow");
86
103
  router.add("POST", "onboarding/disconnect", async ({ res }) => {
87
104
  try {
88
105
  const state = await deps.onboarding.disconnect();
package/dist/index.d.ts CHANGED
@@ -22,6 +22,11 @@ export interface PluginAPI {
22
22
  dashboardEnabled: boolean;
23
23
  }>;
24
24
  };
25
+ "openclaw-plugin"?: {
26
+ config?: Partial<OrgXConfig & {
27
+ dashboardEnabled: boolean;
28
+ }>;
29
+ };
25
30
  };
26
31
  };
27
32
  };