@useorgx/openclaw-plugin 0.7.8 → 0.7.15

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 (203) hide show
  1. package/README.md +94 -122
  2. package/dashboard/dist/assets/0RUEVzJa.js +1 -0
  3. package/dashboard/dist/assets/0RUEVzJa.js.br +0 -0
  4. package/dashboard/dist/assets/0RUEVzJa.js.gz +0 -0
  5. package/dashboard/dist/assets/3TtV4moZ.js +1 -0
  6. package/dashboard/dist/assets/3TtV4moZ.js.br +0 -0
  7. package/dashboard/dist/assets/3TtV4moZ.js.gz +0 -0
  8. package/dashboard/dist/assets/3VwNyxUf.js +1 -0
  9. package/dashboard/dist/assets/3VwNyxUf.js.br +0 -0
  10. package/dashboard/dist/assets/3VwNyxUf.js.gz +0 -0
  11. package/dashboard/dist/assets/{DpuQm1oF.js → 7DhYqBrM.js} +2 -2
  12. package/dashboard/dist/assets/7DhYqBrM.js.br +0 -0
  13. package/dashboard/dist/assets/7DhYqBrM.js.gz +0 -0
  14. package/dashboard/dist/assets/{tcEHYcbW.js → BCudUvwg.js} +1 -1
  15. package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
  16. package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
  17. package/dashboard/dist/assets/BV0BcV1u.js +53 -0
  18. package/dashboard/dist/assets/BV0BcV1u.js.br +0 -0
  19. package/dashboard/dist/assets/BV0BcV1u.js.gz +0 -0
  20. package/dashboard/dist/assets/BVvffj0x.js +1 -0
  21. package/dashboard/dist/assets/BVvffj0x.js.br +0 -0
  22. package/dashboard/dist/assets/BVvffj0x.js.gz +0 -0
  23. package/dashboard/dist/assets/BiOgVMED.js +1 -0
  24. package/dashboard/dist/assets/BiOgVMED.js.br +0 -0
  25. package/dashboard/dist/assets/BiOgVMED.js.gz +0 -0
  26. package/dashboard/dist/assets/BjK42gtU.js +1 -0
  27. package/dashboard/dist/assets/BjK42gtU.js.br +0 -0
  28. package/dashboard/dist/assets/BjK42gtU.js.gz +0 -0
  29. package/dashboard/dist/assets/C-MOJWHs.js +1 -0
  30. package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
  31. package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
  32. package/dashboard/dist/assets/C91KLKit.js +1 -0
  33. package/dashboard/dist/assets/C91KLKit.js.br +0 -0
  34. package/dashboard/dist/assets/C91KLKit.js.gz +0 -0
  35. package/dashboard/dist/assets/{CnitK1MX.js → C9fvfXmS.js} +1 -1
  36. package/dashboard/dist/assets/C9fvfXmS.js.br +0 -0
  37. package/dashboard/dist/assets/C9fvfXmS.js.gz +0 -0
  38. package/dashboard/dist/assets/CFZ4Swr5.js +1 -0
  39. package/dashboard/dist/assets/CFZ4Swr5.js.br +0 -0
  40. package/dashboard/dist/assets/CFZ4Swr5.js.gz +0 -0
  41. package/dashboard/dist/assets/{77gGFBt6.js → CGj8kRhg.js} +1 -1
  42. package/dashboard/dist/assets/CGj8kRhg.js.br +0 -0
  43. package/dashboard/dist/assets/CGj8kRhg.js.gz +0 -0
  44. package/dashboard/dist/assets/CJjEAGFN.js +1 -0
  45. package/dashboard/dist/assets/CJjEAGFN.js.br +0 -0
  46. package/dashboard/dist/assets/CJjEAGFN.js.gz +0 -0
  47. package/dashboard/dist/assets/CKrH5fYO.js +1 -0
  48. package/dashboard/dist/assets/CKrH5fYO.js.br +0 -0
  49. package/dashboard/dist/assets/CKrH5fYO.js.gz +0 -0
  50. package/dashboard/dist/assets/CMTTPXch.js +1 -0
  51. package/dashboard/dist/assets/CMTTPXch.js.br +0 -0
  52. package/dashboard/dist/assets/CMTTPXch.js.gz +0 -0
  53. package/dashboard/dist/assets/CSlBSRyv.js +1 -0
  54. package/dashboard/dist/assets/CSlBSRyv.js.br +0 -0
  55. package/dashboard/dist/assets/CSlBSRyv.js.gz +0 -0
  56. package/dashboard/dist/assets/CnPC783_.js +1 -0
  57. package/dashboard/dist/assets/CnPC783_.js.br +0 -0
  58. package/dashboard/dist/assets/CnPC783_.js.gz +0 -0
  59. package/dashboard/dist/assets/Ctw95IkC.js +1 -0
  60. package/dashboard/dist/assets/Ctw95IkC.js.br +0 -0
  61. package/dashboard/dist/assets/Ctw95IkC.js.gz +0 -0
  62. package/dashboard/dist/assets/DHz-aQPw.js +1 -0
  63. package/dashboard/dist/assets/DHz-aQPw.js.br +0 -0
  64. package/dashboard/dist/assets/DHz-aQPw.js.gz +0 -0
  65. package/dashboard/dist/assets/DNX2foSJ.css +1 -0
  66. package/dashboard/dist/assets/DNX2foSJ.css.br +0 -0
  67. package/dashboard/dist/assets/DNX2foSJ.css.gz +0 -0
  68. package/dashboard/dist/assets/Dj2k1r16.js +8 -0
  69. package/dashboard/dist/assets/Dj2k1r16.js.br +0 -0
  70. package/dashboard/dist/assets/Dj2k1r16.js.gz +0 -0
  71. package/dashboard/dist/assets/DxUw4FMR.js +212 -0
  72. package/dashboard/dist/assets/DxUw4FMR.js.br +0 -0
  73. package/dashboard/dist/assets/DxUw4FMR.js.gz +0 -0
  74. package/dashboard/dist/assets/T2NFtzAv.js +1 -0
  75. package/dashboard/dist/assets/T2NFtzAv.js.br +0 -0
  76. package/dashboard/dist/assets/T2NFtzAv.js.gz +0 -0
  77. package/dashboard/dist/assets/cX2e-TLi.js +1 -0
  78. package/dashboard/dist/assets/cX2e-TLi.js.br +0 -0
  79. package/dashboard/dist/assets/cX2e-TLi.js.gz +0 -0
  80. package/dashboard/dist/assets/eeHXe_OQ.js +9 -0
  81. package/dashboard/dist/assets/eeHXe_OQ.js.br +0 -0
  82. package/dashboard/dist/assets/eeHXe_OQ.js.gz +0 -0
  83. package/dashboard/dist/assets/{DxKG5zy8.js → gZr_xKlA.js} +2 -2
  84. package/dashboard/dist/assets/gZr_xKlA.js.br +0 -0
  85. package/dashboard/dist/assets/gZr_xKlA.js.gz +0 -0
  86. package/dashboard/dist/brand/control-tower.png +0 -0
  87. package/dashboard/dist/brand/design-codex.png +0 -0
  88. package/dashboard/dist/brand/engineering-autopilot.png +0 -0
  89. package/dashboard/dist/brand/launch-captain.png +0 -0
  90. package/dashboard/dist/brand/orgx-logo.png +0 -0
  91. package/dashboard/dist/brand/pipeline-intelligence.png +0 -0
  92. package/dashboard/dist/brand/product-orchestrator.png +0 -0
  93. package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
  94. package/dashboard/dist/index.html +8 -6
  95. package/dashboard/dist/index.html.br +0 -0
  96. package/dashboard/dist/index.html.gz +0 -0
  97. package/dist/hash-utils.d.ts +1 -0
  98. package/dist/hash-utils.js +4 -0
  99. package/dist/http/helpers/auto-continue-engine.d.ts +36 -0
  100. package/dist/http/helpers/auto-continue-engine.js +198 -75
  101. package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
  102. package/dist/http/helpers/autopilot-runtime.js +31 -3
  103. package/dist/http/helpers/autopilot-slice-utils.d.ts +10 -0
  104. package/dist/http/helpers/autopilot-slice-utils.js +158 -54
  105. package/dist/http/helpers/hash-utils.d.ts +1 -1
  106. package/dist/http/helpers/hash-utils.js +1 -1
  107. package/dist/http/helpers/humanize-slice-failure.d.ts +35 -0
  108. package/dist/http/helpers/humanize-slice-failure.js +137 -0
  109. package/dist/http/helpers/mission-control.d.ts +1 -0
  110. package/dist/http/helpers/mission-control.js +73 -7
  111. package/dist/http/helpers/queue-constants.d.ts +37 -0
  112. package/dist/http/helpers/queue-constants.js +34 -0
  113. package/dist/http/helpers/slice-experience-v2.js +2 -5
  114. package/dist/http/helpers/slice-run-projections.js +2 -5
  115. package/dist/http/helpers/workspace-scope.js +4 -3
  116. package/dist/http/index.js +166 -63
  117. package/dist/http/routes/chat.js +1 -21
  118. package/dist/http/routes/live-misc.js +9 -2
  119. package/dist/http/routes/live-snapshot.js +14 -27
  120. package/dist/http/routes/mission-control-actions.js +7 -18
  121. package/dist/http/routes/mission-control-read.d.ts +1 -0
  122. package/dist/http/routes/mission-control-read.js +14 -56
  123. package/dist/index.d.ts +8 -1
  124. package/dist/index.js +21 -1
  125. package/dist/lib/type-coercion.d.ts +10 -0
  126. package/dist/lib/type-coercion.js +82 -0
  127. package/dist/mcp-http-handler.js +14 -2
  128. package/dist/openclaw.plugin.json +1 -1
  129. package/dist/services/experiment-randomization.js +9 -2
  130. package/dist/tools/core-tools.d.ts +27 -0
  131. package/dist/tools/core-tools.js +89 -0
  132. package/openclaw.plugin.json +1 -1
  133. package/package.json +3 -2
  134. package/dashboard/dist/assets/77gGFBt6.js.br +0 -0
  135. package/dashboard/dist/assets/77gGFBt6.js.gz +0 -0
  136. package/dashboard/dist/assets/BBpTN_SR.js +0 -1
  137. package/dashboard/dist/assets/BBpTN_SR.js.br +0 -0
  138. package/dashboard/dist/assets/BBpTN_SR.js.gz +0 -0
  139. package/dashboard/dist/assets/BJgZIVUQ.js +0 -53
  140. package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
  141. package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
  142. package/dashboard/dist/assets/BTAEErUY.js +0 -1
  143. package/dashboard/dist/assets/BTAEErUY.js.br +0 -0
  144. package/dashboard/dist/assets/BTAEErUY.js.gz +0 -0
  145. package/dashboard/dist/assets/BVShoyjA.js +0 -1
  146. package/dashboard/dist/assets/BVShoyjA.js.br +0 -0
  147. package/dashboard/dist/assets/BVShoyjA.js.gz +0 -0
  148. package/dashboard/dist/assets/BgcAY5rE.js +0 -1
  149. package/dashboard/dist/assets/BgcAY5rE.js.br +0 -0
  150. package/dashboard/dist/assets/BgcAY5rE.js.gz +0 -0
  151. package/dashboard/dist/assets/C-KIc3Wc.js +0 -1
  152. package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
  153. package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
  154. package/dashboard/dist/assets/C-PAoJF-.js +0 -1
  155. package/dashboard/dist/assets/C-PAoJF-.js.br +0 -0
  156. package/dashboard/dist/assets/C-PAoJF-.js.gz +0 -0
  157. package/dashboard/dist/assets/C0nA-iUG.js +0 -1
  158. package/dashboard/dist/assets/C0nA-iUG.js.br +0 -0
  159. package/dashboard/dist/assets/C0nA-iUG.js.gz +0 -0
  160. package/dashboard/dist/assets/C6GO-FKy.js +0 -1
  161. package/dashboard/dist/assets/C6GO-FKy.js.br +0 -0
  162. package/dashboard/dist/assets/C6GO-FKy.js.gz +0 -0
  163. package/dashboard/dist/assets/CFwPph5U.js +0 -1
  164. package/dashboard/dist/assets/CFwPph5U.js.br +0 -0
  165. package/dashboard/dist/assets/CFwPph5U.js.gz +0 -0
  166. package/dashboard/dist/assets/CL_wXqR7.js +0 -1
  167. package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
  168. package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
  169. package/dashboard/dist/assets/CPjsbbgZ.js +0 -212
  170. package/dashboard/dist/assets/CPjsbbgZ.js.br +0 -0
  171. package/dashboard/dist/assets/CPjsbbgZ.js.gz +0 -0
  172. package/dashboard/dist/assets/CSr2ZnTV.js +0 -1
  173. package/dashboard/dist/assets/CSr2ZnTV.js.br +0 -0
  174. package/dashboard/dist/assets/CSr2ZnTV.js.gz +0 -0
  175. package/dashboard/dist/assets/CgQDT6yL.js +0 -1
  176. package/dashboard/dist/assets/CgQDT6yL.js.br +0 -0
  177. package/dashboard/dist/assets/CgQDT6yL.js.gz +0 -0
  178. package/dashboard/dist/assets/CnitK1MX.js.br +0 -0
  179. package/dashboard/dist/assets/CnitK1MX.js.gz +0 -0
  180. package/dashboard/dist/assets/CxQ08qFN.js +0 -9
  181. package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
  182. package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
  183. package/dashboard/dist/assets/D7DHFX0D.js +0 -1
  184. package/dashboard/dist/assets/D7DHFX0D.js.br +0 -0
  185. package/dashboard/dist/assets/D7DHFX0D.js.gz +0 -0
  186. package/dashboard/dist/assets/DEip7uko.js +0 -1
  187. package/dashboard/dist/assets/DEip7uko.js.br +0 -0
  188. package/dashboard/dist/assets/DEip7uko.js.gz +0 -0
  189. package/dashboard/dist/assets/DHUSLc01.css +0 -1
  190. package/dashboard/dist/assets/DHUSLc01.css.br +0 -0
  191. package/dashboard/dist/assets/DHUSLc01.css.gz +0 -0
  192. package/dashboard/dist/assets/DOFL9l8s.js +0 -1
  193. package/dashboard/dist/assets/DOFL9l8s.js.br +0 -0
  194. package/dashboard/dist/assets/DOFL9l8s.js.gz +0 -0
  195. package/dashboard/dist/assets/DpuQm1oF.js.br +0 -0
  196. package/dashboard/dist/assets/DpuQm1oF.js.gz +0 -0
  197. package/dashboard/dist/assets/DxKG5zy8.js.br +0 -0
  198. package/dashboard/dist/assets/DxKG5zy8.js.gz +0 -0
  199. package/dashboard/dist/assets/cNrhgGc1.js +0 -8
  200. package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
  201. package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
  202. package/dashboard/dist/assets/tcEHYcbW.js.br +0 -0
  203. package/dashboard/dist/assets/tcEHYcbW.js.gz +0 -0
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { resolveWorkspaceScope as resolveCanonicalWorkspaceScope } from "../helpers/workspace-scope.js";
3
+ import { asRecord, asString, asStringArray } from "../../lib/type-coercion.js";
3
4
  import { buildDispatchGatewayEnvelope } from "./dispatch-gateway-envelope.js";
4
5
  const PLAY_QUEUE_LOOKUP_TIMEOUT_MS = (() => {
5
6
  const raw = process.env.ORGX_PLAY_QUEUE_LOOKUP_TIMEOUT_MS;
@@ -194,24 +195,7 @@ function shouldResetTaskStatus(status, states) {
194
195
  }
195
196
  return false;
196
197
  }
197
- function asRecord(value) {
198
- if (!value || typeof value !== "object" || Array.isArray(value))
199
- return null;
200
- return value;
201
- }
202
- function asString(value) {
203
- if (typeof value !== "string")
204
- return null;
205
- const trimmed = value.trim();
206
- return trimmed.length > 0 ? trimmed : null;
207
- }
208
- function asStringArray(value) {
209
- if (!Array.isArray(value))
210
- return [];
211
- return value
212
- .map((entry) => asString(entry))
213
- .filter((entry) => Boolean(entry));
214
- }
198
+ // asRecord, asString, asStringArray imported from ../../lib/type-coercion.js
215
199
  function parseCycleGraphNodes(graph) {
216
200
  const root = asRecord(graph);
217
201
  const rawNodes = Array.isArray(root?.nodes) ? root.nodes : [];
@@ -456,6 +440,7 @@ export function registerMissionControlActionsRoutes(router, deps) {
456
440
  ignoreSpawnGuardRateLimit: ignoreSpawnGuardRateLimit === true,
457
441
  scope,
458
442
  });
443
+ deps.clearNextUpQueueCache(initiativeId);
459
444
  const dispatchId = randomUUID();
460
445
  const playDispatchEnvelope = (dispatchMode) => buildDispatchGatewayEnvelope({
461
446
  dispatchId,
@@ -1593,6 +1578,7 @@ export function registerMissionControlActionsRoutes(router, deps) {
1593
1578
  ignoreSpawnGuardRateLimit: ignoreSpawnGuardRateLimit === true,
1594
1579
  scope: startScope,
1595
1580
  });
1581
+ deps.clearNextUpQueueCache(initiativeId);
1596
1582
  const dispatchEnvelope = buildDispatchGatewayEnvelope({
1597
1583
  dispatchMode: "server",
1598
1584
  route: "mission-control.auto-continue.start",
@@ -1643,6 +1629,7 @@ export function registerMissionControlActionsRoutes(router, deps) {
1643
1629
  // best effort
1644
1630
  }
1645
1631
  }
1632
+ deps.clearNextUpQueueCache(initiativeId);
1646
1633
  deps.sendJson(res, 200, { ok: true, run });
1647
1634
  }
1648
1635
  catch (err) {
@@ -1664,10 +1651,12 @@ export function registerMissionControlActionsRoutes(router, deps) {
1664
1651
  return;
1665
1652
  }
1666
1653
  await deps.tickAutoContinueRun(run);
1654
+ deps.clearNextUpQueueCache(initiativeId);
1667
1655
  deps.sendJson(res, 200, { ok: true, initiativeId, run });
1668
1656
  return;
1669
1657
  }
1670
1658
  await deps.tickAllAutoContinue();
1659
+ deps.clearNextUpQueueCache(null);
1671
1660
  deps.sendJson(res, 200, { ok: true });
1672
1661
  }
1673
1662
  catch (err) {
@@ -5,6 +5,7 @@ type AutoContinueRunRecord = {
5
5
  status?: string;
6
6
  startedAt?: string;
7
7
  stoppedAt?: string | null;
8
+ updatedAt?: string | null;
8
9
  tokenBudget?: number | null;
9
10
  maxParallelSlices?: number;
10
11
  parallelMode?: string;
@@ -1,34 +1,7 @@
1
1
  import { listBuiltInSentinels } from "../helpers/sentinel-catalog.js";
2
2
  import { resolveWorkspaceScope, workspaceScopeFromHeaders, } from "../helpers/workspace-scope.js";
3
- function asRecord(value) {
4
- if (!value || typeof value !== "object" || Array.isArray(value))
5
- return null;
6
- return value;
7
- }
8
- function asString(value) {
9
- if (typeof value !== "string")
10
- return null;
11
- const trimmed = value.trim();
12
- return trimmed.length > 0 ? trimmed : null;
13
- }
14
- function asArray(value) {
15
- if (Array.isArray(value))
16
- return value;
17
- if (typeof value !== "string")
18
- return [];
19
- const trimmed = value.trim();
20
- try {
21
- const parsed = JSON.parse(trimmed);
22
- if (Array.isArray(parsed))
23
- return parsed;
24
- if (parsed && typeof parsed === "object")
25
- return [parsed];
26
- return [];
27
- }
28
- catch {
29
- return [];
30
- }
31
- }
3
+ import { asRecord, asString, asNumber, asArray, asStringArray, } from "../../lib/type-coercion.js";
4
+ // asRecord, asString, asArray, asNumber, asStringArray imported from ../../lib/type-coercion.js
32
5
  function normalizeRunnerValue(value) {
33
6
  const raw = asString(value);
34
7
  if (!raw)
@@ -105,29 +78,7 @@ function mergeRunnerAgents(...groups) {
105
78
  }
106
79
  return output;
107
80
  }
108
- function asNumber(value) {
109
- if (typeof value === "number" && Number.isFinite(value))
110
- return value;
111
- if (typeof value === "string" && value.trim().length > 0) {
112
- const parsed = Number(value);
113
- if (Number.isFinite(parsed))
114
- return parsed;
115
- }
116
- return null;
117
- }
118
- function asStringArray(value) {
119
- const entries = asArray(value);
120
- if (entries.length === 0)
121
- return [];
122
- const values = [];
123
- for (const entry of entries) {
124
- const normalized = asString(entry);
125
- if (!normalized)
126
- continue;
127
- values.push(normalized);
128
- }
129
- return dedupeStrings(values);
130
- }
81
+ // asNumber, asStringArray imported from ../../lib/type-coercion.js
131
82
  function isCanonicalAllScopeMismatch(canonicalRecord, useAllScope) {
132
83
  if (!useAllScope)
133
84
  return false;
@@ -838,11 +789,18 @@ export function registerMissionControlReadRoutes(router, deps) {
838
789
  const sendRouteException = (res, location, err) => {
839
790
  sendRouteError(res, 500, location, deps.safeErrorMessage(err));
840
791
  };
841
- async function renderAutoContinueStatus(query, res) {
792
+ async function renderAutoContinueStatus(query, res, headerScope) {
793
+ const workspaceScope = resolveWorkspaceScope(query, headerScope, {
794
+ allowProjectScope: false,
795
+ });
796
+ if (workspaceScope.error) {
797
+ sendRouteError(res, 400, "mission-control.read.auto-continue.status.validation", workspaceScope.error);
798
+ return;
799
+ }
842
800
  const initiativeId = query.get("initiative_id") ?? query.get("initiativeId") ?? "";
843
801
  const id = initiativeId.trim();
844
802
  if (!id) {
845
- sendRouteError(res, 400, "mission-control.read.auto-continue.status.validation", "Query parameter 'initiative_id' is required.");
803
+ sendRouteError(res, 400, "mission-control.read.auto-continue.status.validation", "initiativeId is required");
846
804
  return;
847
805
  }
848
806
  const run = deps.autoContinueRuns.get(id) ?? null;
@@ -1743,8 +1701,8 @@ export function registerMissionControlReadRoutes(router, deps) {
1743
1701
  items,
1744
1702
  });
1745
1703
  }
1746
- router.add("GET", "mission-control/auto-continue/status", async ({ query, res }) => renderAutoContinueStatus(query, res), "Get auto-continue status for an initiative");
1747
- router.add("HEAD", "mission-control/auto-continue/status", async ({ query, res }) => renderAutoContinueStatus(query, res), "Get auto-continue status for an initiative (HEAD)");
1704
+ router.add("GET", "mission-control/auto-continue/status", async ({ query, res, req }) => renderAutoContinueStatus(query, res, workspaceScopeFromHeaders(req?.headers)), "Get auto-continue status for an initiative");
1705
+ router.add("HEAD", "mission-control/auto-continue/status", async ({ query, res, req }) => renderAutoContinueStatus(query, res, workspaceScopeFromHeaders(req?.headers)), "Get auto-continue status for an initiative (HEAD)");
1748
1706
  router.add("GET", "mission-control/graph", async ({ query, res }) => renderMissionControlGraph(query, res), "Get mission-control dependency graph");
1749
1707
  router.add("HEAD", "mission-control/graph", async ({ query, res }) => renderMissionControlGraph(query, res), "Get mission-control dependency graph (HEAD)");
1750
1708
  router.add("GET", "mission-control/next-up", async ({ query, res, req }) => renderNextUpQueue(query, res, workspaceScopeFromHeaders(req?.headers)), "Get next-up queue");
package/dist/index.d.ts CHANGED
@@ -48,7 +48,14 @@ export interface PluginAPI {
48
48
  }) => void, options?: {
49
49
  commands?: string[];
50
50
  }) => void;
51
- registerHttpHandler: (handler: unknown) => void;
51
+ registerHttpRoute?: (route: {
52
+ path: string;
53
+ auth: "gateway" | "plugin";
54
+ match?: "exact" | "prefix";
55
+ handler: unknown;
56
+ replaceExisting?: boolean;
57
+ }) => void;
58
+ registerHttpHandler?: (handler: unknown) => void;
52
59
  }
53
60
  export interface ToolResult {
54
61
  content: Array<{
package/dist/index.js CHANGED
@@ -1430,7 +1430,27 @@ export default function register(api) {
1430
1430
  return true;
1431
1431
  return await httpHandler(req, res);
1432
1432
  };
1433
- api.registerHttpHandler(compositeHttpHandler);
1433
+ if (typeof api.registerHttpRoute === "function") {
1434
+ api.registerHttpRoute({
1435
+ path: "/orgx",
1436
+ auth: "plugin",
1437
+ match: "prefix",
1438
+ handler: compositeHttpHandler,
1439
+ });
1440
+ api.registerHttpRoute({
1441
+ path: "/workspace-hub",
1442
+ auth: "plugin",
1443
+ match: "prefix",
1444
+ handler: compositeHttpHandler,
1445
+ });
1446
+ }
1447
+ else if (typeof api.registerHttpHandler === "function") {
1448
+ // Backward compatibility for OpenClaw builds before route-based plugin HTTP registration.
1449
+ api.registerHttpHandler(compositeHttpHandler);
1450
+ }
1451
+ else {
1452
+ throw new Error("OpenClaw plugin API does not expose an HTTP registration method.");
1453
+ }
1434
1454
  api.log?.info?.("[orgx] Plugin registered", {
1435
1455
  baseUrl: config.baseUrl,
1436
1456
  hasApiKey: !!config.apiKey,
@@ -0,0 +1,10 @@
1
+ /** Safely cast unknown to a Record, or null if not a plain object. */
2
+ export declare function asRecord(value: unknown): Record<string, unknown> | null;
3
+ /** Safely cast unknown to a trimmed non-empty string, or null. */
4
+ export declare function asString(value: unknown): string | null;
5
+ /** Safely cast unknown to a finite number, or null. */
6
+ export declare function asNumber(value: unknown): number | null;
7
+ /** Safely cast unknown to an array. Attempts JSON parse for string values. */
8
+ export declare function asArray(value: unknown): unknown[];
9
+ /** Extract a deduplicated string array from unknown. */
10
+ export declare function asStringArray(value: unknown): string[];
@@ -0,0 +1,82 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Shared type-coercion helpers for safe parsing of unknown API data.
3
+ // Previously duplicated across 20+ files.
4
+ // ---------------------------------------------------------------------------
5
+ /** Safely cast unknown to a Record, or null if not a plain object. */
6
+ export function asRecord(value) {
7
+ if (!value || typeof value !== "object" || Array.isArray(value))
8
+ return null;
9
+ const proto = Object.getPrototypeOf(value);
10
+ if (proto !== Object.prototype && proto !== null)
11
+ return null;
12
+ return value;
13
+ }
14
+ /** Safely cast unknown to a trimmed non-empty string, or null. */
15
+ export function asString(value) {
16
+ if (typeof value !== "string")
17
+ return null;
18
+ const trimmed = value.trim();
19
+ return trimmed.length > 0 ? trimmed : null;
20
+ }
21
+ /** Safely cast unknown to a finite number, or null. */
22
+ export function asNumber(value) {
23
+ if (typeof value === "number" && Number.isFinite(value))
24
+ return value;
25
+ if (typeof value === "string" && value.trim().length > 0) {
26
+ const parsed = Number(value);
27
+ if (Number.isFinite(parsed))
28
+ return parsed;
29
+ }
30
+ return null;
31
+ }
32
+ /** Safely cast unknown to an array. Attempts JSON parse for string values. */
33
+ export function asArray(value) {
34
+ if (Array.isArray(value))
35
+ return value;
36
+ if (asRecord(value))
37
+ return [value];
38
+ if (typeof value !== "string")
39
+ return [];
40
+ const trimmed = value.trim();
41
+ try {
42
+ const parsed = JSON.parse(trimmed);
43
+ if (Array.isArray(parsed))
44
+ return parsed;
45
+ if (asRecord(parsed))
46
+ return [parsed];
47
+ return [];
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ }
53
+ /** Extract a deduplicated string array from unknown. */
54
+ export function asStringArray(value) {
55
+ const entries = asArray(value);
56
+ if (entries.length === 0) {
57
+ const scalar = asString(value);
58
+ if (!scalar)
59
+ return [];
60
+ const fallbackEntries = scalar.includes(",") ? scalar.split(",") : [scalar];
61
+ const seenFallback = new Set();
62
+ const fallbackValues = [];
63
+ for (const entry of fallbackEntries) {
64
+ const normalized = asString(entry);
65
+ if (!normalized || seenFallback.has(normalized))
66
+ continue;
67
+ seenFallback.add(normalized);
68
+ fallbackValues.push(normalized);
69
+ }
70
+ return fallbackValues;
71
+ }
72
+ const seen = new Set();
73
+ const values = [];
74
+ for (const entry of entries) {
75
+ const normalized = asString(entry);
76
+ if (!normalized || seen.has(normalized))
77
+ continue;
78
+ seen.add(normalized);
79
+ values.push(normalized);
80
+ }
81
+ return values;
82
+ }
@@ -136,6 +136,13 @@ async function readRequestBodyBuffer(req) {
136
136
  return Buffer.from("", "utf8");
137
137
  return await new Promise((resolve) => {
138
138
  const chunks = [];
139
+ let settled = false;
140
+ const settle = () => {
141
+ if (settled)
142
+ return;
143
+ settled = true;
144
+ resolve(Buffer.concat(chunks));
145
+ };
139
146
  const onData = (chunk) => {
140
147
  if (typeof chunk === "string") {
141
148
  chunks.push(Buffer.from(chunk, "utf8"));
@@ -147,9 +154,14 @@ async function readRequestBodyBuffer(req) {
147
154
  chunks.push(Buffer.from(chunk));
148
155
  }
149
156
  };
150
- const onEnd = () => resolve(Buffer.concat(chunks));
151
- const onError = () => resolve(Buffer.concat(chunks));
157
+ const onEnd = () => settle();
158
+ const onError = () => settle();
152
159
  req.on?.("data", onData);
160
+ if (typeof req.once === "function") {
161
+ req.once("end", onEnd);
162
+ req.once("error", onError);
163
+ return;
164
+ }
153
165
  req.on?.("end", onEnd);
154
166
  req.on?.("error", onError);
155
167
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-plugin",
3
3
  "name": "OrgX Integration",
4
- "version": "1.0.4",
4
+ "version": "1.0.7",
5
5
  "description": "Connects Clawdbot to OrgX for agent orchestration, quality gates, and model routing",
6
6
  "entry": "./index.js",
7
7
  "author": "OrgX Team",
@@ -23,8 +23,13 @@ function normalizeArms(arms) {
23
23
  if (!Array.isArray(arms) || arms.length === 0) {
24
24
  throw new Error("arms must include at least one entry");
25
25
  }
26
+ const seenIds = new Set();
26
27
  const sanitized = arms.map((arm, index) => {
27
28
  const id = sanitizeIdentifier(arm?.id, `arms[${index}].id`);
29
+ if (seenIds.has(id)) {
30
+ throw new Error(`arms[${index}].id must be unique`);
31
+ }
32
+ seenIds.add(id);
28
33
  const weight = Number.isFinite(arm?.weight) ? Number(arm.weight) : Number.NaN;
29
34
  if (!Number.isFinite(weight) || weight <= 0) {
30
35
  throw new Error(`arms[${index}].weight must be > 0`);
@@ -58,6 +63,8 @@ function sanitizeIdentifier(value, field) {
58
63
  return trimmed;
59
64
  }
60
65
  function bucketFromHash(hash) {
61
- const first53Bits = hash.slice(0, 14);
62
- return parseInt(first53Bits, 16) / MAX_UINT53;
66
+ // Keep only 53 high-order bits from the 56-bit hex prefix.
67
+ const first56Bits = parseInt(hash.slice(0, 14), 16);
68
+ const first53Bits = Math.floor(first56Bits / 8);
69
+ return first53Bits / (MAX_UINT53 + 1);
63
70
  }
@@ -68,5 +68,32 @@ export interface RegisterCoreToolsDeps {
68
68
  auditId?: string;
69
69
  }) => import("../skill-pack-state.js").SkillPackState;
70
70
  randomUUID?: () => string;
71
+ sessionStore?: {
72
+ workstreamSessionStore: Map<string, {
73
+ sessionId: string;
74
+ workstreamId: string;
75
+ initiativeId: string;
76
+ sourceClient: string;
77
+ capturedAt: string;
78
+ fromRunId: string;
79
+ }>;
80
+ getWorkstreamSession: (workstreamId: string) => {
81
+ sessionId: string;
82
+ workstreamId: string;
83
+ initiativeId: string;
84
+ sourceClient: string;
85
+ capturedAt: string;
86
+ fromRunId: string;
87
+ } | null;
88
+ setWorkstreamSession: (workstreamId: string, entry: {
89
+ sessionId: string;
90
+ workstreamId: string;
91
+ initiativeId: string;
92
+ sourceClient: string;
93
+ capturedAt: string;
94
+ fromRunId: string;
95
+ }) => void;
96
+ clearWorkstreamSession: (initiativeId: string) => void;
97
+ };
71
98
  }
72
99
  export declare function registerCoreTools(deps: RegisterCoreToolsDeps): Map<string, RegisteredTool>;
@@ -2266,5 +2266,94 @@ export function registerCoreTools(deps) {
2266
2266
  }
2267
2267
  },
2268
2268
  }, { optional: true });
2269
+ // --- orgx_agent_sessions ---
2270
+ registerMcpTool({
2271
+ name: "orgx_agent_sessions",
2272
+ description: "List active CLI session IDs stored for workstreams. Used for session resume support.",
2273
+ parameters: {
2274
+ type: "object",
2275
+ properties: {
2276
+ initiativeId: {
2277
+ type: "string",
2278
+ description: "Optional initiative ID filter.",
2279
+ },
2280
+ },
2281
+ additionalProperties: false,
2282
+ },
2283
+ async execute(_callId, params = {}) {
2284
+ const store = deps.sessionStore;
2285
+ if (!store) {
2286
+ return text("Session store not available.");
2287
+ }
2288
+ const entries = [];
2289
+ for (const [, entry] of store.workstreamSessionStore.entries()) {
2290
+ if (params.initiativeId && entry.initiativeId !== params.initiativeId)
2291
+ continue;
2292
+ entries.push({ ...entry });
2293
+ }
2294
+ return json("Agent sessions:", {
2295
+ count: entries.length,
2296
+ sessions: entries,
2297
+ });
2298
+ },
2299
+ }, { optional: true });
2300
+ // --- orgx_resume_agent_session ---
2301
+ registerMcpTool({
2302
+ name: "orgx_resume_agent_session",
2303
+ description: "Store or update a CLI session ID for a workstream so the next slice resumes it.",
2304
+ parameters: {
2305
+ type: "object",
2306
+ properties: {
2307
+ workstreamId: { type: "string", description: "Workstream UUID" },
2308
+ sessionId: { type: "string", description: "CLI session UUID to resume" },
2309
+ initiativeId: { type: "string", description: "Initiative UUID" },
2310
+ sourceClient: { type: "string", description: "Source client (codex, claude-code)" },
2311
+ },
2312
+ required: ["workstreamId", "sessionId", "initiativeId"],
2313
+ additionalProperties: false,
2314
+ },
2315
+ async execute(_callId, params = { workstreamId: "", sessionId: "", initiativeId: "" }) {
2316
+ const store = deps.sessionStore;
2317
+ if (!store) {
2318
+ return text("Session store not available.");
2319
+ }
2320
+ if (!params.workstreamId || !params.sessionId || !params.initiativeId) {
2321
+ return text("Missing required parameters: workstreamId, sessionId, initiativeId.");
2322
+ }
2323
+ store.setWorkstreamSession(params.workstreamId, {
2324
+ sessionId: params.sessionId,
2325
+ workstreamId: params.workstreamId,
2326
+ initiativeId: params.initiativeId,
2327
+ sourceClient: params.sourceClient ?? "unknown",
2328
+ capturedAt: new Date().toISOString(),
2329
+ fromRunId: "manual",
2330
+ });
2331
+ return text(`Session ${params.sessionId} stored for workstream ${params.workstreamId}.`);
2332
+ },
2333
+ }, { optional: true });
2334
+ // --- orgx_clear_agent_session ---
2335
+ registerMcpTool({
2336
+ name: "orgx_clear_agent_session",
2337
+ description: "Clear stored CLI session IDs for an initiative (forces fresh sessions on next dispatch).",
2338
+ parameters: {
2339
+ type: "object",
2340
+ properties: {
2341
+ initiativeId: { type: "string", description: "Initiative UUID" },
2342
+ },
2343
+ required: ["initiativeId"],
2344
+ additionalProperties: false,
2345
+ },
2346
+ async execute(_callId, params = { initiativeId: "" }) {
2347
+ const store = deps.sessionStore;
2348
+ if (!store) {
2349
+ return text("Session store not available.");
2350
+ }
2351
+ if (!params.initiativeId) {
2352
+ return text("Missing required parameter: initiativeId.");
2353
+ }
2354
+ store.clearWorkstreamSession(params.initiativeId);
2355
+ return text(`Sessions cleared for initiative ${params.initiativeId}.`);
2356
+ },
2357
+ }, { optional: true });
2269
2358
  return mcpToolRegistry;
2270
2359
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-plugin",
3
3
  "name": "OrgX Integration",
4
- "version": "1.0.4",
4
+ "version": "1.0.7",
5
5
  "description": "Connects Clawdbot to OrgX for agent orchestration, quality gates, and model routing",
6
6
  "entry": "./dist/index.js",
7
7
  "author": "OrgX Team",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useorgx/openclaw-plugin",
3
- "version": "0.7.8",
3
+ "version": "0.7.15",
4
4
  "description": "OrgX plugin for OpenClaw — agent orchestration, quality gates, model routing, and live dashboard",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,6 +52,7 @@
52
52
  "verify:iwmt-cascade": "npm run build:core && node ./scripts/verify-iwmt-cascade-e2e.mjs",
53
53
  "verify:live-ui:p0": "node ./scripts/agent-browser-live-ui-p0-audit.mjs",
54
54
  "verify:conduit-mcp": "node ./scripts/verify-conduit-mcp.mjs",
55
+ "verify:repo-hygiene": "node ./scripts/verify-repo-hygiene.mjs",
55
56
  "dev:main": "node ./scripts/dev-main-sync.mjs",
56
57
  "e2e:auto-continue": "node ./scripts/e2e-auto-continue.mjs",
57
58
  "e2e:agent-suite": "npm run build:core && node ./scripts/e2e-agent-suite-kickoff-3x.mjs",
@@ -94,7 +95,7 @@
94
95
  },
95
96
  "repository": {
96
97
  "type": "git",
97
- "url": "https://github.com/useorgx/openclaw-plugin.git"
98
+ "url": "git+https://github.com/useorgx/openclaw-plugin.git"
98
99
  },
99
100
  "homepage": "https://useorgx.com",
100
101
  "bugs": {
@@ -1 +0,0 @@
1
- import{r as l,a as c}from"./cNrhgGc1.js";import{b as g}from"./CPjsbbgZ.js";function f({authToken:s=null,embedMode:o=!1,enabled:n=!0}={}){var a;const i=l.useMemo(()=>["usage-control-plane",{authToken:s,embedMode:o}],[s,o]),e=c({queryKey:i,enabled:n,queryFn:async()=>{const t=await fetch("/orgx/api/usage/control-plane/summary",{headers:g({authToken:s,embedMode:o})}),r=await t.json().catch(()=>null);if(!t.ok){const u=(r&&typeof r=="object"&&"error"in r&&typeof r.error=="string"?r.error:null)??(r&&typeof r=="object"&&"message"in r&&typeof r.message=="string"?r.message:null)??`Failed to load usage summary (${t.status})`;throw new Error(u)}if(!r||typeof r!="object"||!("generatedAt"in r))throw new Error("Usage summary response is missing required fields.");return r},refetchInterval:6e4});return{summary:e.data??null,isLoading:e.isLoading,isFetching:e.isFetching,error:((a=e.error)==null?void 0:a.message)??null,refetch:e.refetch}}export{f as u};