@useorgx/openclaw-plugin 0.4.9 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -11
- package/dashboard/dist/assets/6mILZQ2a.js +1 -0
- package/dashboard/dist/assets/6mILZQ2a.js.br +0 -0
- package/dashboard/dist/assets/6mILZQ2a.js.gz +0 -0
- package/dashboard/dist/assets/8dksYiq4.js +2 -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 +1 -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 +1 -0
- package/dashboard/dist/assets/B6wPWJ35.js.br +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.gz +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js +53 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js +1 -0
- package/dashboard/dist/assets/BWEwjt1W.js.br +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js.gz +0 -0
- package/dashboard/dist/assets/BgOYB78t.js +4 -0
- package/dashboard/dist/assets/BgOYB78t.js.br +0 -0
- package/dashboard/dist/assets/BgOYB78t.js.gz +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css +1 -0
- package/dashboard/dist/assets/BzRbDCAD.css.br +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css.gz +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js +1 -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 +212 -0
- package/dashboard/dist/assets/C9jy61eu.js.br +0 -0
- package/dashboard/dist/assets/C9jy61eu.js.gz +0 -0
- package/dashboard/dist/assets/CC63EwFD.js +1 -0
- package/dashboard/dist/assets/CC63EwFD.js.br +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.gz +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js +1 -0
- package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js +1 -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 +1 -0
- package/dashboard/dist/assets/CgaottFX.js.br +0 -0
- package/dashboard/dist/assets/CgaottFX.js.gz +0 -0
- package/dashboard/dist/assets/{CpJsfbXo.js → CxQ08qFN.js} +2 -2
- package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js +1 -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 +1 -0
- package/dashboard/dist/assets/D3iMTYEj.js.br +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js.gz +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js +2 -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 +1 -0
- package/dashboard/dist/assets/DnA8dpj6.js.br +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js.gz +0 -0
- package/dashboard/dist/assets/IUexzymk.js +1 -0
- package/dashboard/dist/assets/IUexzymk.js.br +0 -0
- package/dashboard/dist/assets/IUexzymk.js.gz +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js +8 -0
- package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +1 -0
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css +1 -0
- package/dashboard/dist/assets/qm8xLgv-.css.br +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css.gz +0 -0
- package/dashboard/dist/assets/rttbDbEx.js +1 -0
- package/dashboard/dist/assets/rttbDbEx.js.br +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.gz +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.br +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openai-mark.svg.br +0 -0
- package/dashboard/dist/brand/openai-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.br +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.gz +0 -0
- package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
- package/dashboard/dist/index.html +7 -5
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/activity-actor-fields.js +26 -4
- package/dist/activity-store.js +34 -8
- package/dist/agent-context-store.js +79 -17
- package/dist/agent-run-store.js +44 -3
- package/dist/agent-suite.d.ts +9 -0
- package/dist/agent-suite.js +149 -9
- package/dist/artifacts/artifact-domain-schemas.d.ts +66 -0
- package/dist/artifacts/artifact-domain-schemas.js +357 -0
- package/dist/artifacts/register-artifact.d.ts +4 -3
- package/dist/artifacts/register-artifact.js +170 -57
- package/dist/chat-store.d.ts +157 -0
- package/dist/chat-store.js +586 -0
- package/dist/cli/orgx.js +11 -0
- package/dist/contracts/client.d.ts +43 -3
- package/dist/contracts/client.js +159 -30
- package/dist/contracts/practice-exercise-schema.d.ts +216 -0
- package/dist/contracts/practice-exercise-schema.js +314 -0
- package/dist/contracts/retro-schema.d.ts +81 -0
- package/dist/contracts/retro-schema.js +80 -0
- package/dist/contracts/shared-types.d.ts +159 -0
- package/dist/contracts/shared-types.js +199 -1
- package/dist/contracts/skill-pack-schema.d.ts +192 -0
- package/dist/contracts/skill-pack-schema.js +180 -0
- package/dist/contracts/types.d.ts +247 -2
- package/dist/entities/auto-assignment.js +43 -17
- package/dist/event-sanitization.d.ts +11 -0
- package/dist/event-sanitization.js +113 -0
- package/dist/gateway-watchdog.d.ts +5 -0
- package/dist/gateway-watchdog.js +50 -0
- package/dist/hooks/post-reporting-event.mjs +1 -5
- package/dist/http/helpers/activity-headline.js +13 -132
- package/dist/http/helpers/auto-continue-engine.d.ts +198 -10
- package/dist/http/helpers/auto-continue-engine.js +3145 -186
- package/dist/http/helpers/autopilot-operations.d.ts +19 -0
- package/dist/http/helpers/autopilot-operations.js +182 -31
- package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
- package/dist/http/helpers/autopilot-runtime.js +328 -25
- package/dist/http/helpers/autopilot-slice-utils.d.ts +18 -0
- package/dist/http/helpers/autopilot-slice-utils.js +514 -93
- package/dist/http/helpers/decision-mapper.d.ts +40 -0
- package/dist/http/helpers/decision-mapper.js +223 -7
- package/dist/http/helpers/dispatch-lifecycle.d.ts +19 -2
- package/dist/http/helpers/dispatch-lifecycle.js +242 -37
- package/dist/http/helpers/kickoff-context.js +104 -0
- package/dist/http/helpers/llm-client.d.ts +47 -0
- package/dist/http/helpers/llm-client.js +256 -0
- package/dist/http/helpers/mission-control.d.ts +102 -3
- package/dist/http/helpers/mission-control.js +498 -9
- package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
- package/dist/http/helpers/sentinel-catalog.js +193 -0
- package/dist/http/helpers/session-classification.d.ts +9 -0
- package/dist/http/helpers/session-classification.js +564 -0
- package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
- package/dist/http/helpers/slice-experience-v2.js +677 -0
- package/dist/http/helpers/slice-run-projections.d.ts +72 -0
- package/dist/http/helpers/slice-run-projections.js +877 -0
- package/dist/http/helpers/triage-mapper.d.ts +43 -0
- package/dist/http/helpers/triage-mapper.js +549 -0
- package/dist/http/helpers/value-utils.js +7 -2
- package/dist/http/helpers/workspace-scope.d.ts +15 -0
- package/dist/http/helpers/workspace-scope.js +170 -0
- package/dist/http/index.js +1420 -105
- package/dist/http/routes/agent-suite.d.ts +9 -0
- package/dist/http/routes/agent-suite.js +294 -8
- package/dist/http/routes/agents-catalog.js +64 -19
- package/dist/http/routes/chat.d.ts +19 -0
- package/dist/http/routes/chat.js +522 -0
- package/dist/http/routes/decision-actions.d.ts +8 -1
- package/dist/http/routes/decision-actions.js +42 -5
- package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
- package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
- package/dist/http/routes/entities.d.ts +16 -0
- package/dist/http/routes/entities.js +232 -6
- package/dist/http/routes/live-legacy.d.ts +5 -0
- package/dist/http/routes/live-legacy.js +23 -509
- package/dist/http/routes/live-misc.d.ts +12 -0
- package/dist/http/routes/live-misc.js +251 -31
- package/dist/http/routes/live-snapshot.d.ts +49 -2
- package/dist/http/routes/live-snapshot.js +653 -23
- package/dist/http/routes/live-terminal.d.ts +11 -0
- package/dist/http/routes/live-terminal.js +154 -0
- package/dist/http/routes/live-triage.d.ts +61 -0
- package/dist/http/routes/live-triage.js +192 -0
- package/dist/http/routes/mission-control-actions.d.ts +49 -1
- package/dist/http/routes/mission-control-actions.js +1246 -84
- package/dist/http/routes/mission-control-read.d.ts +48 -3
- package/dist/http/routes/mission-control-read.js +1658 -20
- package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
- package/dist/http/routes/realtime-orchestrator.js +74 -0
- package/dist/http/routes/run-control.d.ts +5 -2
- package/dist/http/routes/run-control.js +10 -0
- package/dist/http/routes/sentinels-catalog.d.ts +7 -0
- package/dist/http/routes/sentinels-catalog.js +24 -0
- package/dist/http/routes/summary.js +10 -3
- package/dist/http/routes/usage.d.ts +24 -0
- package/dist/http/routes/usage.js +362 -0
- package/dist/http/routes/work-artifacts.js +28 -9
- package/dist/index.js +165 -27
- package/dist/local-openclaw.js +29 -6
- package/dist/mcp-client-setup.js +3 -3
- package/dist/mcp-http-handler.d.ts +3 -0
- package/dist/mcp-http-handler.js +34 -60
- package/dist/next-up-queue-store.d.ts +16 -1
- package/dist/next-up-queue-store.js +89 -7
- package/dist/outbox.d.ts +5 -0
- package/dist/outbox.js +113 -9
- package/dist/paths.js +36 -5
- package/dist/reporting/rollups.d.ts +41 -0
- package/dist/reporting/rollups.js +113 -0
- package/dist/retro/domain-templates.d.ts +45 -0
- package/dist/retro/domain-templates.js +297 -0
- package/dist/retro/quality-rubric.d.ts +33 -0
- package/dist/retro/quality-rubric.js +213 -0
- package/dist/runtime-cleanup.d.ts +18 -0
- package/dist/runtime-cleanup.js +87 -0
- package/dist/services/background.d.ts +11 -0
- package/dist/services/background.js +22 -0
- package/dist/services/experiment-randomization.d.ts +21 -0
- package/dist/services/experiment-randomization.js +63 -0
- package/dist/skill-pack-state.d.ts +36 -5
- package/dist/skill-pack-state.js +273 -29
- package/dist/sync/local-agent-telemetry.d.ts +13 -0
- package/dist/sync/local-agent-telemetry.js +128 -0
- package/dist/sync/outbox-replay.js +131 -24
- package/dist/team-context-store.d.ts +23 -0
- package/dist/team-context-store.js +116 -0
- package/dist/telemetry/posthog.js +4 -2
- package/dist/tools/core-tools.d.ts +10 -14
- package/dist/tools/core-tools.js +1289 -24
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/dist/worker-supervisor.js +23 -0
- package/package.json +20 -6
- package/dashboard/dist/assets/B3ziCA02.js +0 -8
- package/dashboard/dist/assets/B5NEElEI.css +0 -1
- package/dashboard/dist/assets/BhapSNAs.js +0 -215
- package/dashboard/dist/assets/iFdvE7lx.js +0 -1
- package/dashboard/dist/assets/jRJsmpYM.js +0 -1
- package/dashboard/dist/assets/sAhvFnpk.js +0 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Router } from "../router.js";
|
|
2
|
+
type RegisterLiveTerminalRoutesDeps<TReq, TRes> = {
|
|
3
|
+
parseJsonRequest: (req: TReq) => Promise<Record<string, unknown>>;
|
|
4
|
+
sendJson: (res: TRes, status: number, payload: unknown) => void;
|
|
5
|
+
safeErrorMessage: (err: unknown) => string;
|
|
6
|
+
};
|
|
7
|
+
export declare function escapeShellSingleQuotedArg(value: string): string;
|
|
8
|
+
export declare function hasParentTraversalSegment(rawPath: string): boolean;
|
|
9
|
+
export declare function resolveSafeLogPath(logsDir: string, rawPath: string): string | null;
|
|
10
|
+
export declare function registerLiveTerminalRoutes<TReq, TRes>(router: Router<Record<string, never>, TReq, TRes>, deps: RegisterLiveTerminalRoutesDeps<TReq, TRes>): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { platform } from "node:os";
|
|
4
|
+
import { join, resolve, sep } from "node:path";
|
|
5
|
+
import { getOrgxPluginConfigDir } from "../../paths.js";
|
|
6
|
+
export function escapeShellSingleQuotedArg(value) {
|
|
7
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
8
|
+
}
|
|
9
|
+
function pickString(input, keys) {
|
|
10
|
+
for (const key of keys) {
|
|
11
|
+
const value = input[key];
|
|
12
|
+
if (typeof value !== "string")
|
|
13
|
+
continue;
|
|
14
|
+
const trimmed = value.trim();
|
|
15
|
+
if (trimmed.length > 0)
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function resolveLogsDir() {
|
|
21
|
+
return resolve(getOrgxPluginConfigDir(), "autopilot-logs");
|
|
22
|
+
}
|
|
23
|
+
export function hasParentTraversalSegment(rawPath) {
|
|
24
|
+
return rawPath.split(/[\\/]+/).some((segment) => segment === "..");
|
|
25
|
+
}
|
|
26
|
+
export function resolveSafeLogPath(logsDir, rawPath) {
|
|
27
|
+
if (rawPath.includes("\0"))
|
|
28
|
+
return null;
|
|
29
|
+
const isAbsolute = rawPath.startsWith("/") || rawPath.startsWith("\\");
|
|
30
|
+
if (!isAbsolute && hasParentTraversalSegment(rawPath))
|
|
31
|
+
return null;
|
|
32
|
+
const candidate = isAbsolute ? resolve(rawPath) : resolve(logsDir, rawPath);
|
|
33
|
+
const base = logsDir.endsWith(sep) ? logsDir : `${logsDir}${sep}`;
|
|
34
|
+
if (!candidate.startsWith(base))
|
|
35
|
+
return null;
|
|
36
|
+
return existsSync(candidate) ? candidate : null;
|
|
37
|
+
}
|
|
38
|
+
function resolveLogPathFromIds(logsDir, ids) {
|
|
39
|
+
for (const rawId of ids) {
|
|
40
|
+
const id = rawId.trim();
|
|
41
|
+
if (!isSafeLogId(id))
|
|
42
|
+
continue;
|
|
43
|
+
const candidates = [join(logsDir, id), join(logsDir, `${id}.log`), join(logsDir, `${id}.output.json`)];
|
|
44
|
+
for (const candidate of candidates) {
|
|
45
|
+
if (existsSync(candidate))
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function isSafeLogId(input) {
|
|
52
|
+
if (input.length === 0 || input.length > 128)
|
|
53
|
+
return false;
|
|
54
|
+
if (input === "." || input === ".." || input.includes(".."))
|
|
55
|
+
return false;
|
|
56
|
+
return /^[A-Za-z0-9._-]+$/.test(input);
|
|
57
|
+
}
|
|
58
|
+
function openPathInTerminal(targetPath) {
|
|
59
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
60
|
+
const os = platform();
|
|
61
|
+
const pathArg = escapeShellSingleQuotedArg(targetPath);
|
|
62
|
+
const tailCmd = `tail -f ${pathArg}`;
|
|
63
|
+
const tailCmdArg = escapeShellSingleQuotedArg(tailCmd);
|
|
64
|
+
let cmd;
|
|
65
|
+
if (os === "darwin") {
|
|
66
|
+
cmd = `osascript -e 'tell application "Terminal" to do script ${JSON.stringify(tailCmd)}'`;
|
|
67
|
+
}
|
|
68
|
+
else if (os === "linux") {
|
|
69
|
+
cmd = `gnome-terminal -- bash -lc ${tailCmdArg} 2>/dev/null || xterm -e bash -lc ${tailCmdArg}`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
rejectPromise(new Error(`Terminal open not supported on ${os}`));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
exec(cmd, (err) => {
|
|
76
|
+
if (err)
|
|
77
|
+
rejectPromise(err);
|
|
78
|
+
else
|
|
79
|
+
resolvePromise();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function openPathInEditor(targetPath) {
|
|
84
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
85
|
+
const pathArg = escapeShellSingleQuotedArg(targetPath);
|
|
86
|
+
const os = platform();
|
|
87
|
+
const cmd = os === "darwin"
|
|
88
|
+
? `cursor ${pathArg} 2>/dev/null || code ${pathArg} 2>/dev/null || open ${pathArg} 2>/dev/null`
|
|
89
|
+
: os === "linux"
|
|
90
|
+
? `cursor ${pathArg} 2>/dev/null || code ${pathArg} 2>/dev/null || xdg-open ${pathArg} 2>/dev/null`
|
|
91
|
+
: "";
|
|
92
|
+
if (!cmd) {
|
|
93
|
+
rejectPromise(new Error(`Editor open not supported on ${os}`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
exec(cmd, (err) => {
|
|
97
|
+
if (err)
|
|
98
|
+
rejectPromise(err);
|
|
99
|
+
else
|
|
100
|
+
resolvePromise();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function resolveTargetPath(payload) {
|
|
105
|
+
const logsDir = resolveLogsDir();
|
|
106
|
+
const explicitPath = pickString(payload, [
|
|
107
|
+
"logPath",
|
|
108
|
+
"log_path",
|
|
109
|
+
"path",
|
|
110
|
+
"sessionPath",
|
|
111
|
+
"session_path",
|
|
112
|
+
]);
|
|
113
|
+
if (explicitPath) {
|
|
114
|
+
const resolved = resolveSafeLogPath(logsDir, explicitPath);
|
|
115
|
+
if (resolved)
|
|
116
|
+
return resolved;
|
|
117
|
+
}
|
|
118
|
+
const ids = [
|
|
119
|
+
pickString(payload, ["sliceRunId", "slice_run_id"]),
|
|
120
|
+
pickString(payload, ["runId", "run_id"]),
|
|
121
|
+
pickString(payload, ["sessionId", "session_id"]),
|
|
122
|
+
].filter((value) => Boolean(value));
|
|
123
|
+
if (ids.length > 0) {
|
|
124
|
+
return resolveLogPathFromIds(logsDir, ids);
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
export function registerLiveTerminalRoutes(router, deps) {
|
|
129
|
+
router.add("POST", "live/terminal/open", async ({ req, res }) => {
|
|
130
|
+
try {
|
|
131
|
+
const payload = await deps.parseJsonRequest(req);
|
|
132
|
+
const targetPath = resolveTargetPath(payload);
|
|
133
|
+
if (!targetPath) {
|
|
134
|
+
deps.sendJson(res, 404, {
|
|
135
|
+
error: "Terminal target not found. Provide runId, sliceRunId, sessionId, or logPath.",
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
await openPathInTerminal(targetPath);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
await openPathInEditor(targetPath);
|
|
144
|
+
}
|
|
145
|
+
deps.sendJson(res, 200, { ok: true, path: targetPath });
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
deps.sendJson(res, 500, { error: deps.safeErrorMessage(err) });
|
|
149
|
+
}
|
|
150
|
+
}, "Open run/session logs in terminal or editor");
|
|
151
|
+
router.add("*", "live/terminal/open", ({ res }) => {
|
|
152
|
+
deps.sendJson(res, 405, { error: "Use POST /orgx/api/live/terminal/open" });
|
|
153
|
+
}, "Reject unsupported methods for live/terminal/open");
|
|
154
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage queue API routes.
|
|
3
|
+
*
|
|
4
|
+
* GET /live/triage – list open triage items (merged decisions + blockers)
|
|
5
|
+
* POST /live/triage/:id/action – act on a triage item
|
|
6
|
+
*/
|
|
7
|
+
import type { LiveDecision } from "../../contracts/shared-types.js";
|
|
8
|
+
interface Router<TState, TReq, TRes> {
|
|
9
|
+
add(method: string, path: string, handler: (ctx: {
|
|
10
|
+
req: TReq;
|
|
11
|
+
res: TRes;
|
|
12
|
+
path: string;
|
|
13
|
+
query: URLSearchParams;
|
|
14
|
+
body: unknown;
|
|
15
|
+
state: TState;
|
|
16
|
+
}) => Promise<void>, description?: string): void;
|
|
17
|
+
}
|
|
18
|
+
export interface RegisterLiveTriageRoutesDeps<TReq, TRes> {
|
|
19
|
+
parseJsonRequest: (req: TReq) => Promise<Record<string, unknown>>;
|
|
20
|
+
sendJson: (res: TRes, status: number, body: unknown) => void;
|
|
21
|
+
getDecisions: (workspaceId?: string | null) => LiveDecision[];
|
|
22
|
+
getBlockerEvents: (workspaceId?: string | null) => Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
failureType: string;
|
|
25
|
+
reason?: string | null;
|
|
26
|
+
provider?: string | null;
|
|
27
|
+
initiativeId?: string | null;
|
|
28
|
+
initiativeTitle?: string | null;
|
|
29
|
+
workstreamId?: string | null;
|
|
30
|
+
workstreamTitle?: string | null;
|
|
31
|
+
taskId?: string | null;
|
|
32
|
+
taskTitle?: string | null;
|
|
33
|
+
agentId?: string | null;
|
|
34
|
+
domain?: string | null;
|
|
35
|
+
sourceSystem?: string | null;
|
|
36
|
+
runId?: string | null;
|
|
37
|
+
logPath?: string | null;
|
|
38
|
+
outputPath?: string | null;
|
|
39
|
+
metadata?: Record<string, unknown>;
|
|
40
|
+
timestamp?: string;
|
|
41
|
+
}>;
|
|
42
|
+
resolveDecisionAction: (decisionId: string, action: string, note?: string | null, optionId?: string | null) => Promise<{
|
|
43
|
+
ok: boolean;
|
|
44
|
+
error?: string;
|
|
45
|
+
}>;
|
|
46
|
+
emitDecisionResolvedActivity?: (input: {
|
|
47
|
+
ids: string[];
|
|
48
|
+
action: "approve" | "reject";
|
|
49
|
+
note?: string | null;
|
|
50
|
+
optionId?: string | null;
|
|
51
|
+
initiativeId?: string | null;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
snoozeTriage?: (itemId: string, durationMinutes: number) => Promise<{
|
|
54
|
+
ok: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
dismissTriage?: (itemId: string, note?: string) => Promise<{
|
|
57
|
+
ok: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
}
|
|
60
|
+
export declare function registerLiveTriageRoutes<TReq, TRes>(router: Router<Record<string, never>, TReq, TRes>, deps: RegisterLiveTriageRoutesDeps<TReq, TRes>): void;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage queue API routes.
|
|
3
|
+
*
|
|
4
|
+
* GET /live/triage – list open triage items (merged decisions + blockers)
|
|
5
|
+
* POST /live/triage/:id/action – act on a triage item
|
|
6
|
+
*/
|
|
7
|
+
import { mapDecisionToTriageItem, mapFailureToTriageItem, deduplicateTriageItems, } from "../helpers/triage-mapper.js";
|
|
8
|
+
export function registerLiveTriageRoutes(router, deps) {
|
|
9
|
+
// ─── GET /live/triage ─────────────────────────────────────────────
|
|
10
|
+
router.add("GET", "live/triage", async ({ res, query }) => {
|
|
11
|
+
const workspaceId = query.get("workspace_id") || null;
|
|
12
|
+
const statusFilter = query.get("status") || "open";
|
|
13
|
+
const limitStr = query.get("limit");
|
|
14
|
+
const limit = limitStr ? Math.min(Math.max(1, parseInt(limitStr, 10) || 50), 200) : 50;
|
|
15
|
+
const degraded = [];
|
|
16
|
+
// 1. Map existing decisions to triage items
|
|
17
|
+
let decisionItems = [];
|
|
18
|
+
try {
|
|
19
|
+
const decisions = deps.getDecisions(workspaceId);
|
|
20
|
+
decisionItems = decisions
|
|
21
|
+
.filter((d) => d.status === "pending")
|
|
22
|
+
.map(mapDecisionToTriageItem);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
degraded.push("decisions");
|
|
26
|
+
}
|
|
27
|
+
// 2. Map blocker events to triage items
|
|
28
|
+
let blockerItems = [];
|
|
29
|
+
try {
|
|
30
|
+
const blockerEvents = deps.getBlockerEvents(workspaceId);
|
|
31
|
+
blockerItems = await Promise.all(blockerEvents.map((event) => mapFailureToTriageItem(event)));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
degraded.push("blockers");
|
|
35
|
+
}
|
|
36
|
+
// 3. Merge and deduplicate
|
|
37
|
+
let allItems = deduplicateTriageItems([...decisionItems, ...blockerItems]);
|
|
38
|
+
// 4. Filter by status
|
|
39
|
+
if (statusFilter !== "all") {
|
|
40
|
+
allItems = allItems.filter((item) => item.status === statusFilter);
|
|
41
|
+
}
|
|
42
|
+
// 5. Sort: critical first, then by impact, then recency
|
|
43
|
+
allItems.sort((a, b) => {
|
|
44
|
+
const severityOrder = {
|
|
45
|
+
critical: 0,
|
|
46
|
+
high: 1,
|
|
47
|
+
medium: 2,
|
|
48
|
+
low: 3,
|
|
49
|
+
};
|
|
50
|
+
const sevDiff = (severityOrder[a.severity] ?? 3) - (severityOrder[b.severity] ?? 3);
|
|
51
|
+
if (sevDiff !== 0)
|
|
52
|
+
return sevDiff;
|
|
53
|
+
const impactA = a.impact.initiativeCount +
|
|
54
|
+
a.impact.workstreamCount +
|
|
55
|
+
a.impact.downstreamBlockedCount;
|
|
56
|
+
const impactB = b.impact.initiativeCount +
|
|
57
|
+
b.impact.workstreamCount +
|
|
58
|
+
b.impact.downstreamBlockedCount;
|
|
59
|
+
if (impactB !== impactA)
|
|
60
|
+
return impactB - impactA;
|
|
61
|
+
return (new Date(b.lastSeenAt).getTime() - new Date(a.lastSeenAt).getTime());
|
|
62
|
+
});
|
|
63
|
+
const total = allItems.length;
|
|
64
|
+
const items = allItems.slice(0, limit);
|
|
65
|
+
const response = {
|
|
66
|
+
ok: true,
|
|
67
|
+
items,
|
|
68
|
+
total,
|
|
69
|
+
generatedAt: new Date().toISOString(),
|
|
70
|
+
...(degraded.length > 0 ? { degraded } : {}),
|
|
71
|
+
};
|
|
72
|
+
deps.sendJson(res, 200, response);
|
|
73
|
+
}, "List triage items (merged decisions + blockers)");
|
|
74
|
+
// ─── POST /live/triage/:id/action ─────────────────────────────────
|
|
75
|
+
router.add("POST", "live/triage/action", async ({ req, res, query }) => {
|
|
76
|
+
const itemId = query.get("id");
|
|
77
|
+
if (!itemId) {
|
|
78
|
+
deps.sendJson(res, 400, { ok: false, error: "Missing triage item id" });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let body;
|
|
82
|
+
try {
|
|
83
|
+
body = await deps.parseJsonRequest(req);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
deps.sendJson(res, 400, { ok: false, error: "Invalid JSON body" });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const action = typeof body.action === "string" ? body.action : null;
|
|
90
|
+
if (!action) {
|
|
91
|
+
deps.sendJson(res, 400, { ok: false, error: "Missing action field" });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const note = typeof body.note === "string" ? body.note : null;
|
|
95
|
+
const optionIdRaw = typeof body.option_id === "string"
|
|
96
|
+
? body.option_id
|
|
97
|
+
: typeof body.optionId === "string"
|
|
98
|
+
? body.optionId
|
|
99
|
+
: null;
|
|
100
|
+
const optionId = typeof optionIdRaw === "string" ? optionIdRaw : null;
|
|
101
|
+
const snoozeDurationMinutes = typeof body.snoozeDurationMinutes === "number"
|
|
102
|
+
? body.snoozeDurationMinutes
|
|
103
|
+
: null;
|
|
104
|
+
const sideEffects = [];
|
|
105
|
+
let itemStatus = "open";
|
|
106
|
+
let continuationPlan = null;
|
|
107
|
+
// Route action based on item type
|
|
108
|
+
if (itemId.startsWith("triage-decision-")) {
|
|
109
|
+
const decisionId = itemId.replace("triage-decision-", "");
|
|
110
|
+
if (action === "approve" || action === "reject") {
|
|
111
|
+
const result = await deps.resolveDecisionAction(decisionId, action, note, optionId);
|
|
112
|
+
if (!result.ok) {
|
|
113
|
+
deps.sendJson(res, 500, {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: result.error ?? "Decision action failed",
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (typeof deps.emitDecisionResolvedActivity === "function") {
|
|
120
|
+
try {
|
|
121
|
+
await deps.emitDecisionResolvedActivity({
|
|
122
|
+
ids: [decisionId],
|
|
123
|
+
action,
|
|
124
|
+
note,
|
|
125
|
+
optionId,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// best effort
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
itemStatus = "resolved";
|
|
133
|
+
sideEffects.push(action === "approve"
|
|
134
|
+
? "Decision approved; agent will continue."
|
|
135
|
+
: "Decision rejected; agent paused.");
|
|
136
|
+
continuationPlan =
|
|
137
|
+
action === "approve"
|
|
138
|
+
? "Agent will resume with approved direction."
|
|
139
|
+
: null;
|
|
140
|
+
}
|
|
141
|
+
else if (action === "snooze" && deps.snoozeTriage) {
|
|
142
|
+
await deps.snoozeTriage(itemId, snoozeDurationMinutes ?? 60);
|
|
143
|
+
itemStatus = "snoozed";
|
|
144
|
+
sideEffects.push(`Snoozed for ${snoozeDurationMinutes ?? 60} minutes.`);
|
|
145
|
+
}
|
|
146
|
+
else if (action === "dismiss" && deps.dismissTriage) {
|
|
147
|
+
await deps.dismissTriage(itemId, note ?? undefined);
|
|
148
|
+
itemStatus = "dismissed";
|
|
149
|
+
sideEffects.push("Dismissed. Will not re-raise until root cause changes.");
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
deps.sendJson(res, 400, {
|
|
153
|
+
ok: false,
|
|
154
|
+
error: `Unsupported action: ${action}`,
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Non-decision triage items
|
|
161
|
+
if (action === "snooze" && deps.snoozeTriage) {
|
|
162
|
+
await deps.snoozeTriage(itemId, snoozeDurationMinutes ?? 60);
|
|
163
|
+
itemStatus = "snoozed";
|
|
164
|
+
sideEffects.push(`Snoozed for ${snoozeDurationMinutes ?? 60} minutes.`);
|
|
165
|
+
}
|
|
166
|
+
else if (action === "dismiss" && deps.dismissTriage) {
|
|
167
|
+
await deps.dismissTriage(itemId, note ?? undefined);
|
|
168
|
+
itemStatus = "dismissed";
|
|
169
|
+
sideEffects.push("Dismissed.");
|
|
170
|
+
}
|
|
171
|
+
else if (action === "retry") {
|
|
172
|
+
itemStatus = "open";
|
|
173
|
+
sideEffects.push("Retry queued.");
|
|
174
|
+
continuationPlan = "Will re-attempt the failed operation.";
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
deps.sendJson(res, 400, {
|
|
178
|
+
ok: false,
|
|
179
|
+
error: `Unsupported action: ${action} for item ${itemId}`,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const response = {
|
|
185
|
+
ok: true,
|
|
186
|
+
itemStatus: itemStatus,
|
|
187
|
+
continuationPlan,
|
|
188
|
+
sideEffects,
|
|
189
|
+
};
|
|
190
|
+
deps.sendJson(res, 200, response);
|
|
191
|
+
}, "Perform action on a triage item");
|
|
192
|
+
}
|
|
@@ -9,7 +9,9 @@ type AutoContinueRunRecord = Record<string, any> & {
|
|
|
9
9
|
};
|
|
10
10
|
type NextUpQueue = {
|
|
11
11
|
items: Array<{
|
|
12
|
+
initiativeId: string;
|
|
12
13
|
workstreamId: string;
|
|
14
|
+
queueState: "queued" | "running" | "blocked" | "idle";
|
|
13
15
|
runnerAgentId?: string | null;
|
|
14
16
|
runnerAgentName?: string | null;
|
|
15
17
|
runnerSource?: string | null;
|
|
@@ -17,6 +19,23 @@ type NextUpQueue = {
|
|
|
17
19
|
workstreamTitle?: string | null;
|
|
18
20
|
nextTaskId?: string | null;
|
|
19
21
|
nextTaskTitle?: string | null;
|
|
22
|
+
sliceScope?: "task" | "milestone" | "workstream" | null;
|
|
23
|
+
sliceTaskIds?: string[];
|
|
24
|
+
sliceTaskCount?: number | null;
|
|
25
|
+
sliceMilestoneId?: string | null;
|
|
26
|
+
executionPolicy?: {
|
|
27
|
+
domain?: string;
|
|
28
|
+
requiredSkills?: string[];
|
|
29
|
+
profile?: string | null;
|
|
30
|
+
sliceScopePreference?: "adaptive" | "task" | "milestone" | "workstream" | null;
|
|
31
|
+
maxSliceTasks?: number | null;
|
|
32
|
+
maxParallelAgents?: number | null;
|
|
33
|
+
dependencyMode?: "strict" | "relaxed" | null;
|
|
34
|
+
} | null;
|
|
35
|
+
autoContinue?: {
|
|
36
|
+
status?: string | null;
|
|
37
|
+
stopReason?: string | null;
|
|
38
|
+
} | null;
|
|
20
39
|
}>;
|
|
21
40
|
degraded: string[];
|
|
22
41
|
};
|
|
@@ -29,7 +48,7 @@ type RegisterMissionControlActionsRoutesDeps<TReq, TRes> = {
|
|
|
29
48
|
dedupeStrings: (values: string[]) => string[];
|
|
30
49
|
resolveAgentDisplayName: (agentId: string, fallbackName: string | null) => Promise<string | null>;
|
|
31
50
|
buildNextUpQueue: (input: {
|
|
32
|
-
initiativeId
|
|
51
|
+
initiativeId?: string | null;
|
|
33
52
|
}) => Promise<NextUpQueue>;
|
|
34
53
|
startAutoContinueRun: (input: any) => Promise<AutoContinueRunRecord>;
|
|
35
54
|
autoContinueRuns: Map<string, any>;
|
|
@@ -49,6 +68,24 @@ type RegisterMissionControlActionsRoutesDeps<TReq, TRes> = {
|
|
|
49
68
|
stopAutoContinueRun: (input: any) => Promise<void>;
|
|
50
69
|
updateInitiativeAutoContinueState: (input: any) => Promise<void>;
|
|
51
70
|
tickAllAutoContinue: () => Promise<void>;
|
|
71
|
+
scheduleAutoFixForWorkstream: (input: {
|
|
72
|
+
initiativeId: string;
|
|
73
|
+
workstreamId: string;
|
|
74
|
+
runId?: string | null;
|
|
75
|
+
event?: string | null;
|
|
76
|
+
requestedByAgentId?: string | null;
|
|
77
|
+
requestedByAgentName?: string | null;
|
|
78
|
+
graceMs?: number | null;
|
|
79
|
+
}) => Promise<{
|
|
80
|
+
requestId: string;
|
|
81
|
+
initiativeId: string;
|
|
82
|
+
workstreamId: string;
|
|
83
|
+
runId: string | null;
|
|
84
|
+
sourceEvent: string | null;
|
|
85
|
+
graceMs: number;
|
|
86
|
+
scheduledAt: string;
|
|
87
|
+
dueAt: string;
|
|
88
|
+
}>;
|
|
52
89
|
upsertNextUpQueuePin: (input: {
|
|
53
90
|
initiativeId: string;
|
|
54
91
|
workstreamId: string;
|
|
@@ -65,6 +102,13 @@ type RegisterMissionControlActionsRoutesDeps<TReq, TRes> = {
|
|
|
65
102
|
pins: unknown[];
|
|
66
103
|
updatedAt: string;
|
|
67
104
|
};
|
|
105
|
+
suppressNextUpQueueItem: (input: {
|
|
106
|
+
initiativeId: string;
|
|
107
|
+
workstreamId: string;
|
|
108
|
+
}) => {
|
|
109
|
+
suppressions: unknown[];
|
|
110
|
+
updatedAt: string;
|
|
111
|
+
};
|
|
68
112
|
setNextUpQueuePinOrder: (input: {
|
|
69
113
|
order: Array<{
|
|
70
114
|
initiativeId: string;
|
|
@@ -74,8 +118,12 @@ type RegisterMissionControlActionsRoutesDeps<TReq, TRes> = {
|
|
|
74
118
|
pins: unknown[];
|
|
75
119
|
updatedAt: string;
|
|
76
120
|
};
|
|
121
|
+
clearNextUpQueueCache: (initiativeId?: string | null) => void;
|
|
77
122
|
resolveAutoAssignments: (input: any) => Promise<unknown>;
|
|
123
|
+
buildMissionControlGraph: (initiativeId: string) => Promise<unknown>;
|
|
124
|
+
applyLocalInitiativeOverrideToGraph: (graph: unknown) => unknown;
|
|
78
125
|
client: any;
|
|
126
|
+
rawRequest?: (requestMethod: "GET" | "POST" | "PATCH" | "PUT" | "DELETE", requestPath: string, body?: unknown) => Promise<unknown>;
|
|
79
127
|
sendJson: (res: TRes, status: number, payload: unknown) => void;
|
|
80
128
|
safeErrorMessage: (err: unknown) => string;
|
|
81
129
|
};
|