@useorgx/openclaw-plugin 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/{CgaottFX.js → B1mDqDdy.js} +1 -1
- package/dashboard/dist/assets/B1mDqDdy.js.br +0 -0
- package/dashboard/dist/assets/B1mDqDdy.js.gz +0 -0
- package/dashboard/dist/assets/BDMstdfu.js +1 -0
- package/dashboard/dist/assets/BDMstdfu.js.br +0 -0
- package/dashboard/dist/assets/BDMstdfu.js.gz +0 -0
- package/dashboard/dist/assets/BTAEErUY.js +1 -0
- package/dashboard/dist/assets/BTAEErUY.js.br +0 -0
- package/dashboard/dist/assets/BTAEErUY.js.gz +0 -0
- package/dashboard/dist/assets/{8dksYiq4.js → BXapfumA.js} +1 -1
- package/dashboard/dist/assets/BXapfumA.js.br +0 -0
- package/dashboard/dist/assets/BXapfumA.js.gz +0 -0
- package/dashboard/dist/assets/BZZKPNDb.css +1 -0
- package/dashboard/dist/assets/BZZKPNDb.css.br +0 -0
- package/dashboard/dist/assets/BZZKPNDb.css.gz +0 -0
- package/dashboard/dist/assets/{rttbDbEx.js → BdusVDcP.js} +1 -1
- package/dashboard/dist/assets/BdusVDcP.js.br +0 -0
- package/dashboard/dist/assets/BdusVDcP.js.gz +0 -0
- package/dashboard/dist/assets/{D8JNX8kq.js → BwkNYH6r.js} +1 -1
- package/dashboard/dist/assets/BwkNYH6r.js.br +0 -0
- package/dashboard/dist/assets/BwkNYH6r.js.gz +0 -0
- package/dashboard/dist/assets/{CzCxAZlW.js → CRhvoVeH.js} +1 -1
- package/dashboard/dist/assets/CRhvoVeH.js.br +0 -0
- package/dashboard/dist/assets/CRhvoVeH.js.gz +0 -0
- package/dashboard/dist/assets/{B6wPWJ35.js → CYchPSem.js} +1 -1
- package/dashboard/dist/assets/CYchPSem.js.br +0 -0
- package/dashboard/dist/assets/CYchPSem.js.gz +0 -0
- package/dashboard/dist/assets/Cn07669c.js +212 -0
- package/dashboard/dist/assets/Cn07669c.js.br +0 -0
- package/dashboard/dist/assets/Cn07669c.js.gz +0 -0
- package/dashboard/dist/assets/{C8uM3AX8.js → DBG89Wsz.js} +1 -1
- package/dashboard/dist/assets/DBG89Wsz.js.br +0 -0
- package/dashboard/dist/assets/DBG89Wsz.js.gz +0 -0
- package/dashboard/dist/assets/{CZaT3ob_.js → DHoYGJ9i.js} +1 -1
- package/dashboard/dist/assets/DHoYGJ9i.js.br +0 -0
- package/dashboard/dist/assets/DHoYGJ9i.js.gz +0 -0
- package/dashboard/dist/assets/DHtZPEKd.js +1 -0
- package/dashboard/dist/assets/DHtZPEKd.js.br +0 -0
- package/dashboard/dist/assets/DHtZPEKd.js.gz +0 -0
- package/dashboard/dist/assets/{B5zYRHc3.js → DUaqjbT7.js} +1 -1
- package/dashboard/dist/assets/DUaqjbT7.js.br +0 -0
- package/dashboard/dist/assets/DUaqjbT7.js.gz +0 -0
- package/dashboard/dist/assets/{IUexzymk.js → Dlz0Cm_E.js} +1 -1
- package/dashboard/dist/assets/Dlz0Cm_E.js.br +0 -0
- package/dashboard/dist/assets/Dlz0Cm_E.js.gz +0 -0
- package/dashboard/dist/assets/{CC63EwFD.js → E8q3xNGK.js} +1 -1
- package/dashboard/dist/assets/E8q3xNGK.js.br +0 -0
- package/dashboard/dist/assets/E8q3xNGK.js.gz +0 -0
- package/dashboard/dist/assets/VH0fzk6I.js +1 -0
- package/dashboard/dist/assets/VH0fzk6I.js.br +0 -0
- package/dashboard/dist/assets/VH0fzk6I.js.gz +0 -0
- package/dashboard/dist/assets/bxgRC96y.js +1 -0
- package/dashboard/dist/assets/bxgRC96y.js.br +0 -0
- package/dashboard/dist/assets/bxgRC96y.js.gz +0 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/http/helpers/auto-continue-engine.js +29 -11
- package/dist/http/helpers/autopilot-slice-utils.js +7 -10
- package/dist/http/helpers/openclaw-cli.js +2 -2
- package/dist/http/index.js +160 -4
- package/dist/http/routes/live-legacy.d.ts +2 -0
- package/dist/http/routes/live-legacy.js +97 -0
- package/dist/http/routes/mission-control-read.js +106 -63
- package/dist/openclaw-settings.js +3 -2
- package/dist/openclaw.plugin.json +1 -1
- package/dist/sync/outbox-replay.js +17 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/6mILZQ2a.js +0 -1
- package/dashboard/dist/assets/6mILZQ2a.js.br +0 -0
- package/dashboard/dist/assets/6mILZQ2a.js.gz +0 -0
- package/dashboard/dist/assets/8dksYiq4.js.br +0 -0
- package/dashboard/dist/assets/8dksYiq4.js.gz +0 -0
- package/dashboard/dist/assets/B5zYRHc3.js.br +0 -0
- package/dashboard/dist/assets/B5zYRHc3.js.gz +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.br +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.gz +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js +0 -1
- package/dashboard/dist/assets/BWEwjt1W.js.br +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js.gz +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css +0 -1
- package/dashboard/dist/assets/BzRbDCAD.css.br +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css.gz +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js.br +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js.gz +0 -0
- package/dashboard/dist/assets/C9jy61eu.js +0 -212
- package/dashboard/dist/assets/C9jy61eu.js.br +0 -0
- package/dashboard/dist/assets/C9jy61eu.js.gz +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.br +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.gz +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js.br +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js.gz +0 -0
- package/dashboard/dist/assets/CgaottFX.js.br +0 -0
- package/dashboard/dist/assets/CgaottFX.js.gz +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js.br +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js.gz +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js +0 -1
- package/dashboard/dist/assets/D3iMTYEj.js.br +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js.gz +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js.br +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js.gz +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js +0 -1
- package/dashboard/dist/assets/DnA8dpj6.js.br +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js.gz +0 -0
- package/dashboard/dist/assets/IUexzymk.js.br +0 -0
- package/dashboard/dist/assets/IUexzymk.js.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +0 -1
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.br +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.gz +0 -0
|
@@ -101,6 +101,64 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
101
101
|
router.add("*", "live/filesystem/open", ({ res }) => {
|
|
102
102
|
deps.sendJson(res, 405, { error: "Use GET /orgx/api/live/filesystem/open?path=..." });
|
|
103
103
|
}, "Reject unsupported methods for live/filesystem/open");
|
|
104
|
+
router.add("POST", "live/terminal/open", async ({ body, res }) => {
|
|
105
|
+
const payload = body && typeof body === "object" && !Array.isArray(body)
|
|
106
|
+
? body
|
|
107
|
+
: {};
|
|
108
|
+
const runId = typeof payload.runId === "string" ? payload.runId.trim() : "";
|
|
109
|
+
const rawPath = typeof payload.path === "string" ? payload.path.trim() : "";
|
|
110
|
+
if (!runId && !rawPath) {
|
|
111
|
+
deps.sendJson(res, 400, {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: "runId or path is required",
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let resolvedPath = "";
|
|
118
|
+
if (rawPath) {
|
|
119
|
+
resolvedPath = deps.resolveFilesystemOpenPath(rawPath);
|
|
120
|
+
}
|
|
121
|
+
else if (runId) {
|
|
122
|
+
const candidates = deps.resolveAutopilotLogCandidates
|
|
123
|
+
? deps.resolveAutopilotLogCandidates(runId)
|
|
124
|
+
: [];
|
|
125
|
+
resolvedPath =
|
|
126
|
+
candidates.find((candidate) => deps.existsSync(candidate)) ?? "";
|
|
127
|
+
}
|
|
128
|
+
if (!resolvedPath || !deps.existsSync(resolvedPath)) {
|
|
129
|
+
deps.sendJson(res, 404, {
|
|
130
|
+
ok: false,
|
|
131
|
+
error: "Terminal target not found",
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!deps.openPathInTerminal) {
|
|
136
|
+
deps.sendJson(res, 501, {
|
|
137
|
+
ok: false,
|
|
138
|
+
error: "Terminal open is unavailable in this runtime.",
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await deps.openPathInTerminal(resolvedPath);
|
|
144
|
+
deps.sendJson(res, 200, {
|
|
145
|
+
ok: true,
|
|
146
|
+
path: resolvedPath,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
deps.sendJson(res, 500, {
|
|
151
|
+
ok: false,
|
|
152
|
+
error: deps.safeErrorMessage(err),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}, "Open run logs in local terminal");
|
|
156
|
+
router.add("*", "live/terminal/open", ({ res }) => {
|
|
157
|
+
deps.sendJson(res, 405, {
|
|
158
|
+
ok: false,
|
|
159
|
+
error: "Use POST /orgx/api/live/terminal/open",
|
|
160
|
+
});
|
|
161
|
+
}, "Reject unsupported methods for live/terminal/open");
|
|
104
162
|
async function renderLiveStream(query, req, res) {
|
|
105
163
|
sendDeprecated(res, "/orgx/api/live/stream", "/orgx/api/live/snapshot-v2");
|
|
106
164
|
void query;
|
|
@@ -109,4 +167,43 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
109
167
|
}
|
|
110
168
|
router.add("GET", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream");
|
|
111
169
|
router.add("HEAD", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream (HEAD)");
|
|
170
|
+
router.add("POST", "live/terminal/open", async ({ body, res }) => {
|
|
171
|
+
try {
|
|
172
|
+
const payload = (typeof body === "string" ? JSON.parse(body) : body);
|
|
173
|
+
const runId = payload?.runId;
|
|
174
|
+
if (!runId) {
|
|
175
|
+
deps.sendJson(res, 400, { error: "runId is required" });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const os = await import("os");
|
|
179
|
+
const cp = await import("child_process");
|
|
180
|
+
const path = await import("path");
|
|
181
|
+
const homedir = os.homedir();
|
|
182
|
+
const logPath = path.join(homedir, ".config", "useorgx", "openclaw-plugin", "autopilot-logs", `${runId}.log`);
|
|
183
|
+
if (!deps.existsSync(logPath)) {
|
|
184
|
+
deps.sendJson(res, 404, { error: `Log file not found: ${logPath}` });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const shellPath = logPath.replaceAll("'", "'\\''");
|
|
188
|
+
let command = "";
|
|
189
|
+
if (os.platform() === "darwin") {
|
|
190
|
+
command = `osascript -e 'tell app "Terminal" to do script "tail -f \\"${shellPath}\\""'`;
|
|
191
|
+
}
|
|
192
|
+
else if (os.platform() === "win32") {
|
|
193
|
+
command = `start cmd.exe /k "tail -f \\"${shellPath}\\""`;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
command = `gnome-terminal -- bash -c "tail -f \\"${shellPath}\\"; exec bash"`;
|
|
197
|
+
}
|
|
198
|
+
cp.exec(command, (error) => {
|
|
199
|
+
if (error) {
|
|
200
|
+
console.error("Failed to open terminal:", error);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
deps.sendJson(res, 200, { success: true });
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
deps.sendJson(res, 500, { error: deps.safeErrorMessage(err) });
|
|
207
|
+
}
|
|
208
|
+
}, "Open run session in native terminal");
|
|
112
209
|
}
|
|
@@ -11,6 +11,24 @@ function asString(value) {
|
|
|
11
11
|
const trimmed = value.trim();
|
|
12
12
|
return trimmed.length > 0 ? trimmed : null;
|
|
13
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
|
+
}
|
|
14
32
|
function normalizeRunnerValue(value) {
|
|
15
33
|
const raw = asString(value);
|
|
16
34
|
if (!raw)
|
|
@@ -40,11 +58,12 @@ function normalizeRunnerSource(value) {
|
|
|
40
58
|
return null;
|
|
41
59
|
}
|
|
42
60
|
function normalizeRunnerAgents(value) {
|
|
43
|
-
|
|
61
|
+
const entries = asArray(value);
|
|
62
|
+
if (entries.length === 0)
|
|
44
63
|
return [];
|
|
45
64
|
const output = [];
|
|
46
65
|
const seen = new Set();
|
|
47
|
-
for (const entry of
|
|
66
|
+
for (const entry of entries) {
|
|
48
67
|
const record = asRecord(entry);
|
|
49
68
|
if (!record)
|
|
50
69
|
continue;
|
|
@@ -97,10 +116,11 @@ function asNumber(value) {
|
|
|
97
116
|
return null;
|
|
98
117
|
}
|
|
99
118
|
function asStringArray(value) {
|
|
100
|
-
|
|
119
|
+
const entries = asArray(value);
|
|
120
|
+
if (entries.length === 0)
|
|
101
121
|
return [];
|
|
102
122
|
const values = [];
|
|
103
|
-
for (const entry of
|
|
123
|
+
for (const entry of entries) {
|
|
104
124
|
const normalized = asString(entry);
|
|
105
125
|
if (!normalized)
|
|
106
126
|
continue;
|
|
@@ -198,10 +218,12 @@ function parsePaginationEnvelope(value, fallback) {
|
|
|
198
218
|
const offset = Math.max(0, Math.min(100_000, Math.floor(asNumber(record?.offset) ?? fallback.offset)));
|
|
199
219
|
const limit = Math.max(1, Math.min(300, Math.floor(asNumber(record?.limit) ?? fallback.limit)));
|
|
200
220
|
const total = Math.max(0, Math.floor(asNumber(record?.total) ?? fallback.total));
|
|
201
|
-
const nextCursor = asString(record?.nextCursor);
|
|
221
|
+
const nextCursor = asString(record?.nextCursor) ?? asString(record?.next_cursor);
|
|
202
222
|
const hasMore = typeof record?.hasMore === "boolean"
|
|
203
223
|
? record.hasMore
|
|
204
|
-
:
|
|
224
|
+
: typeof record?.has_more === "boolean"
|
|
225
|
+
? record.has_more
|
|
226
|
+
: offset + limit < total;
|
|
205
227
|
return { offset, limit, total, nextCursor, hasMore };
|
|
206
228
|
}
|
|
207
229
|
const WARMUP_MIN_INTERVAL_MS = 12_000;
|
|
@@ -225,7 +247,9 @@ function shouldRunWarmup(key) {
|
|
|
225
247
|
return true;
|
|
226
248
|
}
|
|
227
249
|
function canonicalReadCacheKey(input) {
|
|
250
|
+
const cacheNamespace = process.env.ORGX_OPENCLAW_PLUGIN_CONFIG_DIR ?? "__default__";
|
|
228
251
|
return [
|
|
252
|
+
cacheNamespace,
|
|
229
253
|
input.route,
|
|
230
254
|
input.workspaceId ?? "__all__",
|
|
231
255
|
input.scopeMode ?? "implicit",
|
|
@@ -339,16 +363,22 @@ async function requestCanonicalWithLegacyFallback(deps, input) {
|
|
|
339
363
|
}
|
|
340
364
|
function normalizeQueueState(value) {
|
|
341
365
|
const normalized = normalizeStatus(asString(value));
|
|
342
|
-
if (normalized === "running" || normalized === "in_progress"
|
|
366
|
+
if (normalized === "running" || normalized === "in_progress") {
|
|
343
367
|
return "running";
|
|
344
368
|
}
|
|
345
|
-
if (normalized === "
|
|
369
|
+
if (normalized === "active" ||
|
|
370
|
+
normalized === "queued" ||
|
|
371
|
+
normalized === "pending" ||
|
|
372
|
+
normalized === "todo" ||
|
|
373
|
+
normalized === "ready") {
|
|
346
374
|
return "queued";
|
|
347
375
|
}
|
|
348
376
|
if (normalized === "blocked" ||
|
|
349
377
|
normalized === "waiting" ||
|
|
350
378
|
normalized === "needs_decision" ||
|
|
351
|
-
normalized === "waiting_on_decision"
|
|
379
|
+
normalized === "waiting_on_decision" ||
|
|
380
|
+
normalized === "waiting_dependency" ||
|
|
381
|
+
normalized === "blocked_by_dependency") {
|
|
352
382
|
return "blocked";
|
|
353
383
|
}
|
|
354
384
|
if (normalized === "completed" || normalized === "done")
|
|
@@ -495,9 +525,11 @@ function normalizeQueueItems(input) {
|
|
|
495
525
|
? runnerSourceHint ?? "inferred"
|
|
496
526
|
: "fallback";
|
|
497
527
|
const queueState = normalizeQueueState(record.queueState ?? record.queue_state);
|
|
498
|
-
const
|
|
499
|
-
const sliceScope =
|
|
500
|
-
|
|
528
|
+
const normalizedSliceScope = normalizeStatus(asString(record.sliceScope) ?? asString(record.slice_scope));
|
|
529
|
+
const sliceScope = normalizedSliceScope === "task" ||
|
|
530
|
+
normalizedSliceScope === "milestone" ||
|
|
531
|
+
normalizedSliceScope === "workstream"
|
|
532
|
+
? normalizedSliceScope
|
|
501
533
|
: null;
|
|
502
534
|
const sliceTaskCountRaw = asNumber(record.sliceTaskCount ?? record.slice_task_count);
|
|
503
535
|
const blockReason = asString(record.blockReason) ??
|
|
@@ -574,58 +606,54 @@ function normalizeQueueItems(input) {
|
|
|
574
606
|
return left.workstreamTitle.localeCompare(right.workstreamTitle);
|
|
575
607
|
});
|
|
576
608
|
}
|
|
577
|
-
function
|
|
609
|
+
function isHighSeverityQueueItem(item) {
|
|
578
610
|
if (item.scoringTier === "urgent")
|
|
579
|
-
return
|
|
580
|
-
if (item.
|
|
581
|
-
return
|
|
582
|
-
const reason = (item.blockReason ?? "").toLowerCase();
|
|
583
|
-
if (!reason)
|
|
584
|
-
return "medium";
|
|
585
|
-
if (/\b(critical|sev-?1|severity\s*1|outage|security|incident|prod(?:uction)?\s+down)\b/.test(reason)) {
|
|
586
|
-
return "high";
|
|
611
|
+
return true;
|
|
612
|
+
if (typeof item.nextTaskPriority === "number" && item.nextTaskPriority <= 2) {
|
|
613
|
+
return true;
|
|
587
614
|
}
|
|
588
|
-
if (
|
|
589
|
-
return
|
|
615
|
+
if (typeof item.initiativePriorityNum === "number" && item.initiativePriorityNum <= 2) {
|
|
616
|
+
return true;
|
|
590
617
|
}
|
|
591
|
-
|
|
618
|
+
if (typeof item.compositeScore === "number" && item.compositeScore >= 80) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
const reason = (item.blockReason ?? "").toLowerCase();
|
|
622
|
+
return /(critical|sev[ -]?0|sev[ -]?1|p0|p1|incident|outage)/.test(reason);
|
|
592
623
|
}
|
|
593
|
-
function
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
624
|
+
function applyQueueNoiseControls(items, options) {
|
|
625
|
+
const filteredByThreshold = options.noiseThreshold === "low"
|
|
626
|
+
? items
|
|
627
|
+
: items.filter((item) => {
|
|
628
|
+
if (item.queueState !== "blocked" && item.queueState !== "idle") {
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
const highSeverity = isHighSeverityQueueItem(item);
|
|
632
|
+
if (options.noiseThreshold === "medium") {
|
|
633
|
+
return highSeverity;
|
|
634
|
+
}
|
|
635
|
+
return item.queueState === "blocked" && highSeverity;
|
|
636
|
+
});
|
|
637
|
+
if (options.dedupWindowMs <= 0)
|
|
638
|
+
return filteredByThreshold;
|
|
606
639
|
const deduped = [];
|
|
607
|
-
const
|
|
608
|
-
for (const item of
|
|
640
|
+
const lastSeenByReason = new Map();
|
|
641
|
+
for (const item of filteredByThreshold) {
|
|
609
642
|
if (item.queueState !== "blocked") {
|
|
610
643
|
deduped.push(item);
|
|
611
644
|
continue;
|
|
612
645
|
}
|
|
613
|
-
const
|
|
614
|
-
if (
|
|
646
|
+
const updated = updatedEpoch(item.updatedAt);
|
|
647
|
+
if (updated <= 0) {
|
|
615
648
|
deduped.push(item);
|
|
616
649
|
continue;
|
|
617
650
|
}
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
continue;
|
|
622
|
-
}
|
|
623
|
-
const dedupKey = `${item.initiativeId}:${reason}`;
|
|
624
|
-
const lastSeen = latestByKey.get(dedupKey);
|
|
625
|
-
if (typeof lastSeen === "number" && Math.abs(updatedAt - lastSeen) <= dedupWindowMs) {
|
|
651
|
+
const reasonKey = `${item.initiativeId}|${(item.blockReason ?? item.nextTaskTitle ?? "blocked").trim().toLowerCase()}`;
|
|
652
|
+
const previous = lastSeenByReason.get(reasonKey);
|
|
653
|
+
if (typeof previous === "number" && Math.abs(updated - previous) <= options.dedupWindowMs) {
|
|
626
654
|
continue;
|
|
627
655
|
}
|
|
628
|
-
|
|
656
|
+
lastSeenByReason.set(reasonKey, updated);
|
|
629
657
|
deduped.push(item);
|
|
630
658
|
}
|
|
631
659
|
return deduped;
|
|
@@ -688,9 +716,9 @@ function mapCanonicalSlicesToQueueItems(input) {
|
|
|
688
716
|
: []);
|
|
689
717
|
const runnerSourceHint = normalizeRunnerSource(record.runnerSource) ?? normalizeRunnerSource(record.runner_source);
|
|
690
718
|
const runnerSource = runnerAgents.length > 0 ? runnerSourceHint ?? "inferred" : "fallback";
|
|
691
|
-
const suggestedScope = asString(dispatch.suggestedScope) ??
|
|
719
|
+
const suggestedScope = normalizeStatus(asString(dispatch.suggestedScope) ??
|
|
692
720
|
asString(dispatch.suggested_scope) ??
|
|
693
|
-
asString(record.level);
|
|
721
|
+
asString(record.level));
|
|
694
722
|
const sliceScope = suggestedScope === "task" ||
|
|
695
723
|
suggestedScope === "milestone" ||
|
|
696
724
|
suggestedScope === "workstream"
|
|
@@ -720,8 +748,14 @@ function mapCanonicalSlicesToQueueItems(input) {
|
|
|
720
748
|
rawStatus,
|
|
721
749
|
nextTaskId: taskId ?? sliceTaskIds[0] ?? null,
|
|
722
750
|
nextTaskTitle: asString(record.nextTaskTitle) ?? asString(record.next_task_title),
|
|
723
|
-
nextTaskPriority: asNumber(record.priorityNum ??
|
|
724
|
-
|
|
751
|
+
nextTaskPriority: asNumber(record.priorityNum ??
|
|
752
|
+
record.priority_num ??
|
|
753
|
+
record.nextTaskPriority ??
|
|
754
|
+
record.next_task_priority),
|
|
755
|
+
nextTaskDueAt: asString(record.dueAt) ??
|
|
756
|
+
asString(record.due_at) ??
|
|
757
|
+
asString(record.nextTaskDueAt) ??
|
|
758
|
+
asString(record.next_task_due_at),
|
|
725
759
|
nextTaskMilestoneId: asString(record.milestoneId) ?? asString(record.milestone_id),
|
|
726
760
|
runnerAgentId: runnerAgentIdRaw,
|
|
727
761
|
runnerAgentName: runnerAgentNameRaw,
|
|
@@ -851,16 +885,23 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
851
885
|
const requestedOrderMode = query.get("order_mode") ?? query.get("orderMode");
|
|
852
886
|
const includeLineage = parseBoolean(query.get("include_lineage") ?? query.get("includeLineage"));
|
|
853
887
|
// Noise reduction params — suppress blocked/idle queue items by severity.
|
|
854
|
-
// noise_threshold: 'low' (show all) | 'medium' (
|
|
888
|
+
// noise_threshold: 'low' (default, show all) | 'medium' (hide low-severity blocked/idle) | 'high' (only critical/high blocked)
|
|
855
889
|
const noiseThresholdRaw = query.get("noise_threshold") ?? query.get("noiseThreshold");
|
|
856
890
|
const noiseThreshold = noiseThresholdRaw === "low" || noiseThresholdRaw === "high"
|
|
857
891
|
? noiseThresholdRaw
|
|
858
|
-
: "medium"
|
|
892
|
+
: noiseThresholdRaw === "medium"
|
|
893
|
+
? "medium"
|
|
894
|
+
: "low";
|
|
859
895
|
// dedup_window: time window in ms for grouping duplicate blocked items (default: 60000)
|
|
860
896
|
const dedupWindowRaw = query.get("dedup_window") ?? query.get("dedupWindow");
|
|
861
897
|
const dedupWindowMs = dedupWindowRaw != null
|
|
862
898
|
? Math.max(0, parseInt(dedupWindowRaw, 10) || 60000)
|
|
863
899
|
: 60000;
|
|
900
|
+
const queueNoiseCacheTag = dedupeStrings([
|
|
901
|
+
includeLineage ? "lineage:1" : "",
|
|
902
|
+
`noise:${noiseThreshold}`,
|
|
903
|
+
`dedup:${dedupWindowMs}`,
|
|
904
|
+
]).join(",");
|
|
864
905
|
const nextUpCanonicalCacheKey = canonicalReadCacheKey({
|
|
865
906
|
route: "next-up",
|
|
866
907
|
workspaceId: projectId,
|
|
@@ -872,9 +913,7 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
872
913
|
scope: requestedSliceLevelContext,
|
|
873
914
|
order: requestedOrderMode,
|
|
874
915
|
mixPolicy: requestedMixPolicy,
|
|
875
|
-
|
|
876
|
-
dedupWindowMs,
|
|
877
|
-
search: includeLineage ? "lineage:1" : null,
|
|
916
|
+
search: queueNoiseCacheTag || null,
|
|
878
917
|
});
|
|
879
918
|
const cachedCanonicalNextUp = readCanonicalReadCache(nextUpCanonicalCacheKey, {
|
|
880
919
|
allowStale: false,
|
|
@@ -922,6 +961,8 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
922
961
|
params.set("offset", String(offset));
|
|
923
962
|
params.set("limit", String(pageSize));
|
|
924
963
|
params.set("include_completed", includeCompleted ? "1" : "0");
|
|
964
|
+
params.set("noise_threshold", noiseThreshold);
|
|
965
|
+
params.set("dedup_window", String(dedupWindowMs));
|
|
925
966
|
if (requestedSliceLevelContext) {
|
|
926
967
|
params.set("slice_level_context", requestedSliceLevelContext);
|
|
927
968
|
}
|
|
@@ -946,7 +987,7 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
946
987
|
if (isCanonicalAllScopeMismatch(canonicalRecord, useAllScope)) {
|
|
947
988
|
throw new Error("canonical next-up all-workspaces scope mismatch");
|
|
948
989
|
}
|
|
949
|
-
const canonicalItems =
|
|
990
|
+
const canonicalItems = applyQueueNoiseControls(normalizeQueueItems(canonicalRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
|
|
950
991
|
const canonicalTotal = Math.max(canonicalItems.length, Math.floor(asNumber(canonicalRecord.total) ?? canonicalItems.length)) ?? canonicalItems.length;
|
|
951
992
|
const canonicalPagination = parsePaginationEnvelope(canonicalRecord.pagination, {
|
|
952
993
|
offset,
|
|
@@ -1055,6 +1096,8 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
1055
1096
|
bridgeParams.set("offset", String(Math.max(0, offset)));
|
|
1056
1097
|
bridgeParams.set("limit", String(Math.min(300, Math.max(pageSize, offset + pageSize))));
|
|
1057
1098
|
bridgeParams.set("include_completed", includeCompleted ? "1" : "0");
|
|
1099
|
+
bridgeParams.set("noise_threshold", noiseThreshold);
|
|
1100
|
+
bridgeParams.set("dedup_window", String(dedupWindowMs));
|
|
1058
1101
|
bridgeParams.set("mix_policy", requestedMixPolicy ?? "iwmt_v1");
|
|
1059
1102
|
if (requestedOrderMode) {
|
|
1060
1103
|
bridgeParams.set("order_mode", requestedOrderMode);
|
|
@@ -1072,7 +1115,7 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
1072
1115
|
if (isCanonicalAllScopeMismatch(canonicalSlicesRecord, useAllScope)) {
|
|
1073
1116
|
throw new Error("canonical slices all-workspaces scope mismatch");
|
|
1074
1117
|
}
|
|
1075
|
-
const bridgedItems =
|
|
1118
|
+
const bridgedItems = applyQueueNoiseControls(mapCanonicalSlicesToQueueItems(canonicalSlicesRecord.items).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
|
|
1076
1119
|
if (bridgedItems.length > 0) {
|
|
1077
1120
|
const paged = applySliceSearchAndPagination({
|
|
1078
1121
|
items: bridgedItems,
|
|
@@ -1115,7 +1158,7 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
1115
1158
|
initiativeId,
|
|
1116
1159
|
projectId,
|
|
1117
1160
|
});
|
|
1118
|
-
const items =
|
|
1161
|
+
const items = applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
|
|
1119
1162
|
const paged = applySliceSearchAndPagination({
|
|
1120
1163
|
items,
|
|
1121
1164
|
searchTerm: "",
|
|
@@ -1148,7 +1191,7 @@ export function registerMissionControlReadRoutes(router, deps) {
|
|
|
1148
1191
|
initiativeId,
|
|
1149
1192
|
projectId,
|
|
1150
1193
|
});
|
|
1151
|
-
const items =
|
|
1194
|
+
const items = applyQueueNoiseControls(normalizeQueueItems(queue.items ?? []).filter((item) => includeCompleted ? true : item.queueState !== "completed"), { noiseThreshold, dedupWindowMs });
|
|
1152
1195
|
const paged = applySliceSearchAndPagination({
|
|
1153
1196
|
items,
|
|
1154
1197
|
searchTerm: "",
|
|
@@ -101,16 +101,17 @@ export function readOpenClawPrimaryModel(raw) {
|
|
|
101
101
|
return primary || null;
|
|
102
102
|
}
|
|
103
103
|
export function readOpenClawGatewayPort(raw) {
|
|
104
|
+
const isValidPort = (value) => Number.isFinite(value) && value >= 1 && value <= 65_535;
|
|
104
105
|
if (!raw)
|
|
105
106
|
return 18789;
|
|
106
107
|
const gateway = readObject(raw.gateway);
|
|
107
108
|
const port = gateway.port;
|
|
108
|
-
if (typeof port === "number" &&
|
|
109
|
+
if (typeof port === "number" && isValidPort(port)) {
|
|
109
110
|
return Math.floor(port);
|
|
110
111
|
}
|
|
111
112
|
if (typeof port === "string") {
|
|
112
113
|
const parsed = Number.parseInt(port, 10);
|
|
113
|
-
if (
|
|
114
|
+
if (isValidPort(parsed)) {
|
|
114
115
|
return parsed;
|
|
115
116
|
}
|
|
116
117
|
}
|
|
@@ -537,9 +537,14 @@ export function createOutboxReplayer(deps) {
|
|
|
537
537
|
continue;
|
|
538
538
|
}
|
|
539
539
|
const remaining = [];
|
|
540
|
+
let replayedCount = 0;
|
|
541
|
+
let droppedCount = 0;
|
|
542
|
+
let deadLetteredCount = 0;
|
|
543
|
+
let failedCount = 0;
|
|
540
544
|
for (const event of pending) {
|
|
541
545
|
const skipReason = classifyOutboxReplaySkip(event);
|
|
542
546
|
if (skipReason) {
|
|
547
|
+
droppedCount += 1;
|
|
543
548
|
await appendOutboxDeadLetter(queue, event, `dropped_before_replay:${skipReason}`, null);
|
|
544
549
|
logger.warn?.("[orgx] Dropping non-replayable outbox event", {
|
|
545
550
|
queue,
|
|
@@ -550,6 +555,7 @@ export function createOutboxReplayer(deps) {
|
|
|
550
555
|
}
|
|
551
556
|
try {
|
|
552
557
|
await replayOutboxEvent(event);
|
|
558
|
+
replayedCount += 1;
|
|
553
559
|
}
|
|
554
560
|
catch (err) {
|
|
555
561
|
hadReplayFailure = true;
|
|
@@ -558,6 +564,7 @@ export function createOutboxReplayer(deps) {
|
|
|
558
564
|
? Math.max(0, Math.floor(event.replayFailures)) + 1
|
|
559
565
|
: 1;
|
|
560
566
|
if (nextFailures >= OUTBOX_MAX_REPLAY_FAILURES) {
|
|
567
|
+
deadLetteredCount += 1;
|
|
561
568
|
await appendOutboxDeadLetter(queue, {
|
|
562
569
|
...event,
|
|
563
570
|
replayFailures: nextFailures,
|
|
@@ -573,6 +580,7 @@ export function createOutboxReplayer(deps) {
|
|
|
573
580
|
});
|
|
574
581
|
continue;
|
|
575
582
|
}
|
|
583
|
+
failedCount += 1;
|
|
576
584
|
remaining.push({
|
|
577
585
|
...event,
|
|
578
586
|
replayFailures: nextFailures,
|
|
@@ -588,14 +596,15 @@ export function createOutboxReplayer(deps) {
|
|
|
588
596
|
}
|
|
589
597
|
}
|
|
590
598
|
await replaceOutbox(queue, remaining);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
+
logger.info?.("[orgx] Processed buffered outbox events", {
|
|
600
|
+
queue,
|
|
601
|
+
pending: pending.length,
|
|
602
|
+
replayed: replayedCount,
|
|
603
|
+
dropped: droppedCount,
|
|
604
|
+
deadLettered: deadLetteredCount,
|
|
605
|
+
failed: failedCount,
|
|
606
|
+
remaining: remaining.length,
|
|
607
|
+
});
|
|
599
608
|
}
|
|
600
609
|
if (hadReplayFailure) {
|
|
601
610
|
deps.writeOutboxReplayState({
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED