@useorgx/openclaw-plugin 0.7.18 → 0.7.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/9gFmK3Kr.js +1 -0
- package/dashboard/dist/assets/9gFmK3Kr.js.br +0 -0
- package/dashboard/dist/assets/9gFmK3Kr.js.gz +0 -0
- package/dashboard/dist/assets/{DS79hzMu.js → BrMXbzQ-.js} +2 -2
- package/dashboard/dist/assets/BrMXbzQ-.js.br +0 -0
- package/dashboard/dist/assets/BrMXbzQ-.js.gz +0 -0
- package/dashboard/dist/assets/By0MIBj_.js +1 -0
- package/dashboard/dist/assets/By0MIBj_.js.br +0 -0
- package/dashboard/dist/assets/By0MIBj_.js.gz +0 -0
- package/dashboard/dist/assets/C1u2SGin.css +1 -0
- package/dashboard/dist/assets/C1u2SGin.css.br +0 -0
- package/dashboard/dist/assets/C1u2SGin.css.gz +0 -0
- package/dashboard/dist/assets/{467jKHFJ.js → CGJiHCIx.js} +1 -1
- package/dashboard/dist/assets/CGJiHCIx.js.br +0 -0
- package/dashboard/dist/assets/CGJiHCIx.js.gz +0 -0
- package/dashboard/dist/assets/CSd4rSuU.js +212 -0
- package/dashboard/dist/assets/CSd4rSuU.js.br +0 -0
- package/dashboard/dist/assets/CSd4rSuU.js.gz +0 -0
- package/dashboard/dist/assets/{5Ihga-4X.js → CZXS5i_5.js} +1 -1
- package/dashboard/dist/assets/CZXS5i_5.js.br +0 -0
- package/dashboard/dist/assets/CZXS5i_5.js.gz +0 -0
- package/dashboard/dist/assets/{a6qcPiWt.js → CbVWL74-.js} +1 -1
- package/dashboard/dist/assets/CbVWL74-.js.br +0 -0
- package/dashboard/dist/assets/CbVWL74-.js.gz +0 -0
- package/dashboard/dist/assets/{qDJ6rqcs.js → D-FuHfT8.js} +1 -1
- package/dashboard/dist/assets/D-FuHfT8.js.br +0 -0
- package/dashboard/dist/assets/D-FuHfT8.js.gz +0 -0
- package/dashboard/dist/assets/{BcJmNILk.js → D0PN5_vY.js} +1 -1
- package/dashboard/dist/assets/D0PN5_vY.js.br +0 -0
- package/dashboard/dist/assets/D0PN5_vY.js.gz +0 -0
- package/dashboard/dist/assets/DDCPrZRt.js +1 -0
- package/dashboard/dist/assets/DDCPrZRt.js.br +0 -0
- package/dashboard/dist/assets/DDCPrZRt.js.gz +0 -0
- package/dashboard/dist/assets/{B71dt9yu.js → DNQ-iFO2.js} +1 -1
- package/dashboard/dist/assets/DNQ-iFO2.js.br +0 -0
- package/dashboard/dist/assets/DNQ-iFO2.js.gz +0 -0
- package/dashboard/dist/assets/{PVi0vr9a.js → DhPuHPK7.js} +1 -1
- package/dashboard/dist/assets/DhPuHPK7.js.br +0 -0
- package/dashboard/dist/assets/DhPuHPK7.js.gz +0 -0
- package/dashboard/dist/assets/Dhz7qPtn.js +1 -0
- package/dashboard/dist/assets/Dhz7qPtn.js.br +0 -0
- package/dashboard/dist/assets/Dhz7qPtn.js.gz +0 -0
- package/dashboard/dist/assets/LOFrVoPD.js +1 -0
- package/dashboard/dist/assets/LOFrVoPD.js.br +0 -0
- package/dashboard/dist/assets/LOFrVoPD.js.gz +0 -0
- package/dashboard/dist/assets/OlLPtzdz.js +1 -0
- package/dashboard/dist/assets/OlLPtzdz.js.br +0 -0
- package/dashboard/dist/assets/OlLPtzdz.js.gz +0 -0
- package/dashboard/dist/assets/{sdoPH_Z1.js → RN4M9u9W.js} +2 -2
- package/dashboard/dist/assets/RN4M9u9W.js.br +0 -0
- package/dashboard/dist/assets/RN4M9u9W.js.gz +0 -0
- package/dashboard/dist/assets/VCHu272d.js +1 -0
- package/dashboard/dist/assets/VCHu272d.js.br +0 -0
- package/dashboard/dist/assets/VCHu272d.js.gz +0 -0
- package/dashboard/dist/assets/m2smti3F.js +1 -0
- package/dashboard/dist/assets/m2smti3F.js.br +0 -0
- package/dashboard/dist/assets/m2smti3F.js.gz +0 -0
- package/dashboard/dist/assets/{C3_j_W9V.js → nra1yvJX.js} +1 -1
- package/dashboard/dist/assets/nra1yvJX.js.br +0 -0
- package/dashboard/dist/assets/nra1yvJX.js.gz +0 -0
- package/dashboard/dist/assets/qLX6NZ-J.js +1 -0
- package/dashboard/dist/assets/qLX6NZ-J.js.br +0 -0
- package/dashboard/dist/assets/qLX6NZ-J.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/agent-run-store.js +162 -24
- package/dist/cli/orgx.d.ts +3 -0
- package/dist/config/resolution.d.ts +7 -0
- package/dist/config/resolution.js +13 -5
- package/dist/contracts/onboarding-state.d.ts +2 -0
- package/dist/contracts/onboarding-state.js +23 -0
- package/dist/contracts/shared-types.d.ts +17 -0
- package/dist/http/helpers/auto-continue-engine.d.ts +62 -0
- package/dist/http/helpers/auto-continue-engine.js +329 -53
- package/dist/http/helpers/autopilot-runtime.js +5 -1
- package/dist/http/helpers/autopilot-slice-utils.js +25 -1
- package/dist/http/helpers/decision-mapper.d.ts +1 -0
- package/dist/http/helpers/decision-mapper.js +19 -2
- package/dist/http/helpers/dispatch-lifecycle.js +3 -0
- package/dist/http/helpers/mission-control.d.ts +1 -0
- package/dist/http/helpers/mission-control.js +5 -2
- package/dist/http/helpers/slice-run-projections.d.ts +27 -0
- package/dist/http/helpers/slice-run-projections.js +198 -10
- package/dist/http/helpers/triage-mapper.js +220 -6
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +94 -46
- package/dist/http/router.js +64 -9
- package/dist/http/routes/live-legacy.d.ts +19 -2
- package/dist/http/routes/live-legacy.js +110 -27
- package/dist/http/routes/live-snapshot.d.ts +16 -2
- package/dist/http/routes/live-snapshot.js +169 -25
- package/dist/http/routes/mission-control-actions.js +28 -0
- package/dist/http/routes/mission-control-read.d.ts +18 -0
- package/dist/http/routes/mission-control-read.js +130 -218
- package/dist/http/routes/onboarding.d.ts +1 -0
- package/dist/http/routes/onboarding.js +17 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +199 -123
- package/dist/outbox.d.ts +0 -2
- package/dist/outbox.js +268 -150
- package/dist/reporting/rollups.js +18 -11
- package/dist/runtime-instance-store.js +212 -58
- package/dist/stores/materialized-snapshot-store.d.ts +18 -0
- package/dist/stores/materialized-snapshot-store.js +91 -0
- package/dist/stores/sqlite-state.d.ts +6 -0
- package/dist/stores/sqlite-state.js +179 -0
- package/package.json +6 -1
- package/dashboard/dist/assets/467jKHFJ.js.br +0 -0
- package/dashboard/dist/assets/467jKHFJ.js.gz +0 -0
- package/dashboard/dist/assets/5Ihga-4X.js.br +0 -0
- package/dashboard/dist/assets/5Ihga-4X.js.gz +0 -0
- package/dashboard/dist/assets/B71dt9yu.js.br +0 -0
- package/dashboard/dist/assets/B71dt9yu.js.gz +0 -0
- package/dashboard/dist/assets/BCudUvwg.js +0 -1
- package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
- package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
- package/dashboard/dist/assets/BEnI6kNR.js +0 -1
- package/dashboard/dist/assets/BEnI6kNR.js.br +0 -0
- package/dashboard/dist/assets/BEnI6kNR.js.gz +0 -0
- package/dashboard/dist/assets/BcJmNILk.js.br +0 -0
- package/dashboard/dist/assets/BcJmNILk.js.gz +0 -0
- package/dashboard/dist/assets/C-MOJWHs.js +0 -1
- package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
- package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
- package/dashboard/dist/assets/C-XuWXGi.js +0 -1
- package/dashboard/dist/assets/C-XuWXGi.js.br +0 -0
- package/dashboard/dist/assets/C-XuWXGi.js.gz +0 -0
- package/dashboard/dist/assets/C3_j_W9V.js.br +0 -0
- package/dashboard/dist/assets/C3_j_W9V.js.gz +0 -0
- package/dashboard/dist/assets/C9-UYhBb.js +0 -1
- package/dashboard/dist/assets/C9-UYhBb.js.br +0 -0
- package/dashboard/dist/assets/C9-UYhBb.js.gz +0 -0
- package/dashboard/dist/assets/C9yV06GS.js +0 -1
- package/dashboard/dist/assets/C9yV06GS.js.br +0 -0
- package/dashboard/dist/assets/C9yV06GS.js.gz +0 -0
- package/dashboard/dist/assets/CReugbyT.js +0 -1
- package/dashboard/dist/assets/CReugbyT.js.br +0 -0
- package/dashboard/dist/assets/CReugbyT.js.gz +0 -0
- package/dashboard/dist/assets/CSDhTbKy.js +0 -1
- package/dashboard/dist/assets/CSDhTbKy.js.br +0 -0
- package/dashboard/dist/assets/CSDhTbKy.js.gz +0 -0
- package/dashboard/dist/assets/CfMS9yIf.js +0 -1
- package/dashboard/dist/assets/CfMS9yIf.js.br +0 -0
- package/dashboard/dist/assets/CfMS9yIf.js.gz +0 -0
- package/dashboard/dist/assets/D2Kqcmv9.js +0 -212
- package/dashboard/dist/assets/D2Kqcmv9.js.br +0 -0
- package/dashboard/dist/assets/D2Kqcmv9.js.gz +0 -0
- package/dashboard/dist/assets/DS79hzMu.js.br +0 -0
- package/dashboard/dist/assets/DS79hzMu.js.gz +0 -0
- package/dashboard/dist/assets/PVi0vr9a.js.br +0 -0
- package/dashboard/dist/assets/PVi0vr9a.js.gz +0 -0
- package/dashboard/dist/assets/RZkbqlJk.css +0 -1
- package/dashboard/dist/assets/RZkbqlJk.css.br +0 -0
- package/dashboard/dist/assets/RZkbqlJk.css.gz +0 -0
- package/dashboard/dist/assets/a6qcPiWt.js.br +0 -0
- package/dashboard/dist/assets/a6qcPiWt.js.gz +0 -0
- package/dashboard/dist/assets/qDJ6rqcs.js.br +0 -0
- package/dashboard/dist/assets/qDJ6rqcs.js.gz +0 -0
- package/dashboard/dist/assets/sdoPH_Z1.js.br +0 -0
- package/dashboard/dist/assets/sdoPH_Z1.js.gz +0 -0
|
@@ -13,6 +13,8 @@ type LocalLiveActivity = {
|
|
|
13
13
|
};
|
|
14
14
|
type LiveActivityPage = {
|
|
15
15
|
activities: LiveActivityItem[];
|
|
16
|
+
total?: number;
|
|
17
|
+
storeUpdatedAt?: string;
|
|
16
18
|
cursor?: string | null;
|
|
17
19
|
nextCursor?: string | null;
|
|
18
20
|
prevCursor?: string | null;
|
|
@@ -34,7 +36,7 @@ type RouteResLike = {
|
|
|
34
36
|
on?: (event: string, listener: () => void) => void;
|
|
35
37
|
once?: (event: string, listener: () => void) => void;
|
|
36
38
|
};
|
|
37
|
-
type RegisterLiveLegacyRoutesDeps<TRes extends RouteResLike> = {
|
|
39
|
+
type RegisterLiveLegacyRoutesDeps<TReq extends RouteReqLike, TRes extends RouteResLike> = {
|
|
38
40
|
getLiveSessions: (input: {
|
|
39
41
|
initiative: string | null;
|
|
40
42
|
projectId: string | null;
|
|
@@ -82,6 +84,15 @@ type RegisterLiveLegacyRoutesDeps<TRes extends RouteResLike> = {
|
|
|
82
84
|
sessionKey: string | null;
|
|
83
85
|
runId: string | null;
|
|
84
86
|
}) => Promise<Record<string, unknown> | null>;
|
|
87
|
+
summarizeActivityHeadline: (input: {
|
|
88
|
+
text: string;
|
|
89
|
+
title: string | null;
|
|
90
|
+
type: string | null;
|
|
91
|
+
}) => Promise<{
|
|
92
|
+
headline: string;
|
|
93
|
+
source: string;
|
|
94
|
+
model: string | null;
|
|
95
|
+
}>;
|
|
85
96
|
sendJson: (res: TRes, status: number, payload: unknown) => void;
|
|
86
97
|
safeErrorMessage: (err: unknown) => string;
|
|
87
98
|
sendHtml: (res: TRes, status: number, html: string) => void;
|
|
@@ -112,6 +123,12 @@ type RegisterLiveLegacyRoutesDeps<TRes extends RouteResLike> = {
|
|
|
112
123
|
};
|
|
113
124
|
isUserScopedApiKey: (apiKey: string) => boolean;
|
|
114
125
|
streamIdleTimeoutMs: number;
|
|
126
|
+
renderLiveStreamV2?: (input: {
|
|
127
|
+
path: string;
|
|
128
|
+
query: URLSearchParams;
|
|
129
|
+
req: TReq;
|
|
130
|
+
res: TRes;
|
|
131
|
+
}) => Promise<void>;
|
|
115
132
|
};
|
|
116
|
-
export declare function registerLiveLegacyRoutes<TReq extends RouteReqLike, TRes extends RouteResLike>(router: Router<Record<string, never>, TReq, TRes>, deps: RegisterLiveLegacyRoutesDeps<TRes>): void;
|
|
133
|
+
export declare function registerLiveLegacyRoutes<TReq extends RouteReqLike, TRes extends RouteResLike>(router: Router<Record<string, never>, TReq, TRes>, deps: RegisterLiveLegacyRoutesDeps<TReq, TRes>): void;
|
|
117
134
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveWorkspaceScope } from "../helpers/workspace-scope.js";
|
|
1
2
|
export function registerLiveLegacyRoutes(router, deps) {
|
|
2
3
|
function toContextBundle(value) {
|
|
3
4
|
return {
|
|
@@ -5,26 +6,40 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
5
6
|
runs: value.runs ?? {},
|
|
6
7
|
};
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
query.get("projectId"),
|
|
17
|
-
];
|
|
18
|
-
for (const candidate of candidates) {
|
|
19
|
-
if (typeof candidate !== "string")
|
|
20
|
-
continue;
|
|
21
|
-
const normalized = candidate.trim();
|
|
22
|
-
if (!normalized || normalized.toLowerCase() === "all")
|
|
23
|
-
continue;
|
|
24
|
-
return normalized;
|
|
9
|
+
function readActivityMetadataValue(metadata, keys) {
|
|
10
|
+
if (!metadata)
|
|
11
|
+
return null;
|
|
12
|
+
for (const key of keys) {
|
|
13
|
+
const value = metadata[key];
|
|
14
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
15
|
+
return value.trim();
|
|
16
|
+
}
|
|
25
17
|
}
|
|
26
18
|
return null;
|
|
27
19
|
}
|
|
20
|
+
function resolveActivityInitiativeId(item) {
|
|
21
|
+
const direct = item.initiativeId?.trim();
|
|
22
|
+
if (direct)
|
|
23
|
+
return direct;
|
|
24
|
+
const metadata = item.metadata && typeof item.metadata === "object" && !Array.isArray(item.metadata)
|
|
25
|
+
? item.metadata
|
|
26
|
+
: null;
|
|
27
|
+
return readActivityMetadataValue(metadata, [
|
|
28
|
+
"initiativeId",
|
|
29
|
+
"initiative_id",
|
|
30
|
+
"initiative",
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
function filterActivityByInitiativeIds(items, initiativeIds) {
|
|
34
|
+
if (!initiativeIds)
|
|
35
|
+
return items;
|
|
36
|
+
if (initiativeIds.size === 0)
|
|
37
|
+
return [];
|
|
38
|
+
return items.filter((item) => {
|
|
39
|
+
const initiativeId = resolveActivityInitiativeId(item);
|
|
40
|
+
return initiativeId ? initiativeIds.has(initiativeId) : false;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
28
43
|
const sendDeprecated = (res, endpoint, replacement) => {
|
|
29
44
|
deps.sendJson(res, 410, {
|
|
30
45
|
error: `${endpoint} is deprecated`,
|
|
@@ -44,11 +59,26 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
44
59
|
const since = query.get("since");
|
|
45
60
|
const until = query.get("until");
|
|
46
61
|
const cursor = query.get("cursor");
|
|
47
|
-
const projectId =
|
|
62
|
+
const projectId = resolveWorkspaceScope(query, null, { allowProjectScope: true }).workspaceId ??
|
|
63
|
+
null;
|
|
48
64
|
const limitRaw = query.get("limit") ? Number(query.get("limit")) : undefined;
|
|
49
65
|
const limit = Number.isFinite(limitRaw)
|
|
50
66
|
? Math.max(1, Math.floor(Number(limitRaw)))
|
|
51
67
|
: 200;
|
|
68
|
+
const scopedInitiativeIds = projectId
|
|
69
|
+
? new Set((await deps.listInitiativeIdsForProject({ projectId }))
|
|
70
|
+
.map((value) => value.trim())
|
|
71
|
+
.filter((value) => value.length > 0))
|
|
72
|
+
: null;
|
|
73
|
+
if (scopedInitiativeIds && scopedInitiativeIds.size === 0) {
|
|
74
|
+
deps.sendJson(res, 200, {
|
|
75
|
+
activities: [],
|
|
76
|
+
total: 0,
|
|
77
|
+
nextCursor: null,
|
|
78
|
+
storeUpdatedAt: null,
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
52
82
|
let page = deps.listActivityPage({
|
|
53
83
|
limit,
|
|
54
84
|
runId: run,
|
|
@@ -60,7 +90,7 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
60
90
|
const ctx = toContextBundle(deps.readAgentContexts());
|
|
61
91
|
page = {
|
|
62
92
|
...page,
|
|
63
|
-
activities: deps.applyAgentContextsToActivity(page.activities, ctx),
|
|
93
|
+
activities: filterActivityByInitiativeIds(deps.applyAgentContextsToActivity(page.activities, ctx), scopedInitiativeIds),
|
|
64
94
|
};
|
|
65
95
|
}
|
|
66
96
|
const warmKey = `${projectId ?? ""}::${run ?? ""}::${since ?? ""}::${until ?? ""}`;
|
|
@@ -94,7 +124,7 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
94
124
|
const ctx = toContextBundle(deps.readAgentContexts());
|
|
95
125
|
page = {
|
|
96
126
|
...page,
|
|
97
|
-
activities: deps.applyAgentContextsToActivity(page.activities, ctx),
|
|
127
|
+
activities: filterActivityByInitiativeIds(deps.applyAgentContextsToActivity(page.activities, ctx), scopedInitiativeIds),
|
|
98
128
|
};
|
|
99
129
|
}
|
|
100
130
|
}
|
|
@@ -102,6 +132,12 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
102
132
|
// best effort
|
|
103
133
|
}
|
|
104
134
|
}
|
|
135
|
+
if (scopedInitiativeIds) {
|
|
136
|
+
page = {
|
|
137
|
+
...page,
|
|
138
|
+
total: page.activities.length,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
105
141
|
deps.sendJson(res, 200, page);
|
|
106
142
|
}
|
|
107
143
|
router.add("GET", "live/activity/page", async ({ query, res }) => renderLiveActivityPage(query, res), "Paginated live activity");
|
|
@@ -114,9 +150,41 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
114
150
|
router.add("GET", "live/activity", async ({ query, res }) => renderLiveActivity(query, res), "Legacy live activity endpoint");
|
|
115
151
|
router.add("HEAD", "live/activity", async ({ query, res }) => renderLiveActivity(query, res), "Legacy live activity endpoint (HEAD)");
|
|
116
152
|
async function renderLiveActivityDetail(query, res) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
153
|
+
try {
|
|
154
|
+
const turnId = (query.get("turnId") ?? "").trim();
|
|
155
|
+
if (!turnId) {
|
|
156
|
+
deps.sendJson(res, 400, { error: "turnId is required" });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const sessionKey = (query.get("sessionKey") ?? "").trim() || null;
|
|
160
|
+
const runId = (query.get("run") ?? query.get("runId") ?? "").trim() || null;
|
|
161
|
+
const detail = await deps.loadLocalTurnDetail({ turnId, sessionKey, runId });
|
|
162
|
+
if (!detail) {
|
|
163
|
+
deps.sendJson(res, 404, { error: "activity detail not found" });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const summary = typeof detail.summary === "string" && detail.summary.trim().length > 0
|
|
167
|
+
? detail.summary.trim()
|
|
168
|
+
: null;
|
|
169
|
+
if (!summary) {
|
|
170
|
+
deps.sendJson(res, 200, { detail, headline: null, headlineSource: null, headlineModel: null });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const headline = await deps.summarizeActivityHeadline({
|
|
174
|
+
text: summary,
|
|
175
|
+
title: null,
|
|
176
|
+
type: "activity",
|
|
177
|
+
});
|
|
178
|
+
deps.sendJson(res, 200, {
|
|
179
|
+
detail,
|
|
180
|
+
headline: headline.headline,
|
|
181
|
+
headlineSource: headline.source,
|
|
182
|
+
headlineModel: headline.model,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
deps.sendJson(res, 500, { error: deps.safeErrorMessage(err) });
|
|
187
|
+
}
|
|
120
188
|
}
|
|
121
189
|
router.add("GET", "live/activity/detail", async ({ query, res }) => renderLiveActivityDetail(query, res), "Detailed activity turn view");
|
|
122
190
|
router.add("HEAD", "live/activity/detail", async ({ query, res }) => renderLiveActivityDetail(query, res), "Detailed activity turn view (HEAD)");
|
|
@@ -246,10 +314,25 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
246
314
|
});
|
|
247
315
|
}, "Reject unsupported methods for live/terminal/open");
|
|
248
316
|
async function renderLiveStream(query, req, res) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
317
|
+
if (typeof deps.renderLiveStreamV2 === "function") {
|
|
318
|
+
await deps.renderLiveStreamV2({
|
|
319
|
+
path: "live/stream",
|
|
320
|
+
query,
|
|
321
|
+
req,
|
|
322
|
+
res,
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const suffix = query.toString();
|
|
327
|
+
const location = suffix
|
|
328
|
+
? `/orgx/api/live/stream-v2?${suffix}`
|
|
329
|
+
: "/orgx/api/live/stream-v2";
|
|
330
|
+
res.writeHead(307, {
|
|
331
|
+
Location: location,
|
|
332
|
+
Deprecation: "true",
|
|
333
|
+
Link: `</orgx/api/live/stream-v2>; rel="successor-version"`,
|
|
334
|
+
});
|
|
335
|
+
res.end();
|
|
253
336
|
}
|
|
254
337
|
router.add("GET", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream");
|
|
255
338
|
router.add("HEAD", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream (HEAD)");
|
|
@@ -15,8 +15,11 @@ type SnapshotPersistState = {
|
|
|
15
15
|
};
|
|
16
16
|
type LiveSnapshotRoutesDeps<TReq, TRes> = {
|
|
17
17
|
parsePositiveInt: (raw: string | null, fallback: number, max?: number) => number;
|
|
18
|
-
readSnapshotResponseCache: (key: string
|
|
18
|
+
readSnapshotResponseCache: (key: string, options?: {
|
|
19
|
+
allowStale?: boolean;
|
|
20
|
+
}) => Record<string, unknown> | null;
|
|
19
21
|
writeSnapshotResponseCache: (key: string, payload: Record<string, unknown>) => void;
|
|
22
|
+
getSnapshotCacheGeneration: () => number;
|
|
20
23
|
safeErrorMessage: (err: unknown) => string;
|
|
21
24
|
readAgentContexts: () => AgentContextState;
|
|
22
25
|
getScopedAgentIds: (contexts: Record<string, AgentLaunchContext>) => Set<string>;
|
|
@@ -141,6 +144,17 @@ type LiveSnapshotRoutesDeps<TReq, TRes> = {
|
|
|
141
144
|
lastTransitionAt: string;
|
|
142
145
|
} | null;
|
|
143
146
|
sendJson: (res: TRes, status: number, payload: unknown) => void;
|
|
147
|
+
securityHeaders: Record<string, string>;
|
|
148
|
+
corsHeaders: Record<string, string>;
|
|
144
149
|
};
|
|
145
|
-
export declare function registerLiveSnapshotRoutes<TReq
|
|
150
|
+
export declare function registerLiveSnapshotRoutes<TReq extends {
|
|
151
|
+
on?: (event: string, listener: () => void) => void;
|
|
152
|
+
}, TRes extends {
|
|
153
|
+
write?: (chunk: string | Buffer) => boolean | void;
|
|
154
|
+
end: (chunk?: string | Buffer) => void;
|
|
155
|
+
writeHead: (statusCode: number, headers?: Record<string, string>) => unknown;
|
|
156
|
+
writableEnded?: boolean;
|
|
157
|
+
on?: (event: string, listener: () => void) => void;
|
|
158
|
+
once?: (event: string, listener: () => void) => void;
|
|
159
|
+
}>(router: Router<Record<string, never>, TReq, TRes>, deps: LiveSnapshotRoutesDeps<TReq, TRes>): void;
|
|
146
160
|
export {};
|
|
@@ -16,6 +16,8 @@ const LIVE_SNAPSHOT_NEXT_UP_TIMEOUT_MS = (() => {
|
|
|
16
16
|
return 350;
|
|
17
17
|
return Math.max(250, Math.min(15_000, Math.floor(raw)));
|
|
18
18
|
})();
|
|
19
|
+
const LIVE_STREAM_REFRESH_INTERVAL_MS = 1_500;
|
|
20
|
+
const LIVE_STREAM_KEEPALIVE_MS = 20_000;
|
|
19
21
|
async function withSoftTimeout(work, timeoutMs, label) {
|
|
20
22
|
let timer = null;
|
|
21
23
|
try {
|
|
@@ -59,6 +61,12 @@ function emptyOutboxStatus() {
|
|
|
59
61
|
lastReplayError: null,
|
|
60
62
|
};
|
|
61
63
|
}
|
|
64
|
+
function writeSseEvent(res, event, payload) {
|
|
65
|
+
if (typeof res.write !== "function")
|
|
66
|
+
return;
|
|
67
|
+
res.write(`event: ${event}\n`);
|
|
68
|
+
res.write(`data: ${JSON.stringify(payload ?? null)}\n\n`);
|
|
69
|
+
}
|
|
62
70
|
function filterSessionsByInitiative(sessions, initiative) {
|
|
63
71
|
if (!initiative || initiative.trim().length === 0)
|
|
64
72
|
return sessions;
|
|
@@ -230,6 +238,7 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
230
238
|
return `${base}:${normalized}`;
|
|
231
239
|
};
|
|
232
240
|
const headerScopeFromRequest = (req) => workspaceScopeFromHeaders(req?.headers);
|
|
241
|
+
const snapshotInflight = new Map();
|
|
233
242
|
function parseSnapshotQuery(query, headerScope) {
|
|
234
243
|
const sessionsLimit = deps.parsePositiveInt(query.get("sessionsLimit") ?? query.get("sessions_limit"), 320, 1000);
|
|
235
244
|
const activityLimit = deps.parsePositiveInt(query.get("activityLimit") ?? query.get("activity_limit"), 600, 2000);
|
|
@@ -269,6 +278,24 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
269
278
|
});
|
|
270
279
|
return false;
|
|
271
280
|
};
|
|
281
|
+
function withSnapshotMeta(payload, input) {
|
|
282
|
+
const existingMeta = payload.meta && typeof payload.meta === "object" && !Array.isArray(payload.meta)
|
|
283
|
+
? payload.meta
|
|
284
|
+
: {};
|
|
285
|
+
return {
|
|
286
|
+
...payload,
|
|
287
|
+
meta: {
|
|
288
|
+
...existingMeta,
|
|
289
|
+
generatedAt: (typeof payload.generatedAt === "string" && payload.generatedAt) ||
|
|
290
|
+
(typeof existingMeta.generatedAt === "string" && existingMeta.generatedAt) ||
|
|
291
|
+
new Date().toISOString(),
|
|
292
|
+
stale: input.stale,
|
|
293
|
+
staleReason: input.stale ? input.staleReason ?? "upstream_unavailable" : null,
|
|
294
|
+
sequence: deps.getSnapshotCacheGeneration(),
|
|
295
|
+
cacheKey: input.cacheKey,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
272
299
|
async function buildSnapshotBundle(query, headerScope) {
|
|
273
300
|
const parsed = parseSnapshotQuery(query, headerScope);
|
|
274
301
|
const { sessionsLimit, activityLimit, decisionsLimit, initiative, projectId, run, since, decisionStatus, includeIdle, scopeError, } = parsed;
|
|
@@ -583,34 +610,21 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
583
610
|
decisionsRaw: decisions,
|
|
584
611
|
};
|
|
585
612
|
}
|
|
586
|
-
async function
|
|
587
|
-
if (!validateWorkspaceScope(query, res, "live.snapshot.validation", headerScope))
|
|
588
|
-
return;
|
|
589
|
-
const snapshotCacheKey = `${path}?${query.toString()}`;
|
|
590
|
-
const cachedSnapshot = deps.readSnapshotResponseCache(snapshotCacheKey);
|
|
591
|
-
if (cachedSnapshot) {
|
|
592
|
-
deps.sendJson(res, 200, cachedSnapshot);
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
613
|
+
async function buildSnapshotPayload(version, path, query, headerScope) {
|
|
595
614
|
const bundle = await buildSnapshotBundle(query, headerScope);
|
|
596
615
|
const parsed = parseSnapshotQuery(query, headerScope);
|
|
597
|
-
deps.writeSnapshotResponseCache(snapshotCacheKey, bundle.payload);
|
|
598
|
-
deps.writeSnapshotResponseCache("live-snapshot", bundle.payload);
|
|
599
|
-
deps.writeSnapshotResponseCache(snapshotAliasKey("live-snapshot", parsed.projectId), bundle.payload);
|
|
600
|
-
deps.sendJson(res, 200, bundle.payload);
|
|
601
|
-
}
|
|
602
|
-
async function renderSnapshotV2(path, query, res, headerScope) {
|
|
603
|
-
if (!validateWorkspaceScope(query, res, "live.snapshot-v2.validation", headerScope))
|
|
604
|
-
return;
|
|
605
616
|
const snapshotCacheKey = `${path}?${query.toString()}`;
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
617
|
+
if (version === "v1") {
|
|
618
|
+
const payload = withSnapshotMeta(bundle.payload, {
|
|
619
|
+
cacheKey: snapshotCacheKey,
|
|
620
|
+
stale: false,
|
|
621
|
+
});
|
|
622
|
+
deps.writeSnapshotResponseCache(snapshotCacheKey, payload);
|
|
623
|
+
deps.writeSnapshotResponseCache("live-snapshot", payload);
|
|
624
|
+
deps.writeSnapshotResponseCache(snapshotAliasKey("live-snapshot", parsed.projectId), payload);
|
|
625
|
+
return payload;
|
|
610
626
|
}
|
|
611
|
-
const
|
|
612
|
-
const parsed = parseSnapshotQuery(query, headerScope);
|
|
613
|
-
const payload = {
|
|
627
|
+
const payload = withSnapshotMeta({
|
|
614
628
|
...bundle.v2,
|
|
615
629
|
sessions: bundle.payload.sessions,
|
|
616
630
|
activity: bundle.payload.activity,
|
|
@@ -622,12 +636,140 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
622
636
|
outbox: bundle.payload.outbox,
|
|
623
637
|
chat: bundle.payload.chat,
|
|
624
638
|
degraded: bundle.payload.degraded,
|
|
625
|
-
}
|
|
639
|
+
}, {
|
|
640
|
+
cacheKey: snapshotCacheKey,
|
|
641
|
+
stale: false,
|
|
642
|
+
});
|
|
626
643
|
deps.writeSnapshotResponseCache(snapshotCacheKey, payload);
|
|
627
644
|
deps.writeSnapshotResponseCache("live-snapshot-v2", payload);
|
|
628
645
|
deps.writeSnapshotResponseCache(snapshotAliasKey("live-snapshot-v2", parsed.projectId), payload);
|
|
646
|
+
return payload;
|
|
647
|
+
}
|
|
648
|
+
async function getSnapshotPayload(version, path, query, headerScope) {
|
|
649
|
+
const snapshotCacheKey = `${path}?${query.toString()}`;
|
|
650
|
+
const cachedSnapshot = deps.readSnapshotResponseCache(snapshotCacheKey);
|
|
651
|
+
if (cachedSnapshot)
|
|
652
|
+
return cachedSnapshot;
|
|
653
|
+
const inFlight = snapshotInflight.get(snapshotCacheKey);
|
|
654
|
+
if (inFlight)
|
|
655
|
+
return await inFlight;
|
|
656
|
+
const work = buildSnapshotPayload(version, path, query, headerScope)
|
|
657
|
+
.catch((err) => {
|
|
658
|
+
const staleSnapshot = deps.readSnapshotResponseCache(snapshotCacheKey, {
|
|
659
|
+
allowStale: true,
|
|
660
|
+
});
|
|
661
|
+
if (staleSnapshot) {
|
|
662
|
+
return withSnapshotMeta(staleSnapshot, {
|
|
663
|
+
cacheKey: snapshotCacheKey,
|
|
664
|
+
stale: true,
|
|
665
|
+
staleReason: deps.safeErrorMessage(err),
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
throw err;
|
|
669
|
+
})
|
|
670
|
+
.finally(() => {
|
|
671
|
+
snapshotInflight.delete(snapshotCacheKey);
|
|
672
|
+
});
|
|
673
|
+
snapshotInflight.set(snapshotCacheKey, work);
|
|
674
|
+
return await work;
|
|
675
|
+
}
|
|
676
|
+
async function renderSnapshot(path, query, res, headerScope) {
|
|
677
|
+
if (!validateWorkspaceScope(query, res, "live.snapshot.validation", headerScope))
|
|
678
|
+
return;
|
|
679
|
+
const payload = await getSnapshotPayload("v1", path, query, headerScope);
|
|
629
680
|
deps.sendJson(res, 200, payload);
|
|
630
681
|
}
|
|
682
|
+
async function renderSnapshotV2(path, query, res, headerScope) {
|
|
683
|
+
if (!validateWorkspaceScope(query, res, "live.snapshot-v2.validation", headerScope))
|
|
684
|
+
return;
|
|
685
|
+
const payload = await getSnapshotPayload("v2", path, query, headerScope);
|
|
686
|
+
deps.sendJson(res, 200, payload);
|
|
687
|
+
}
|
|
688
|
+
async function renderSnapshotStreamV2(path, query, req, res, headerScope) {
|
|
689
|
+
if (!validateWorkspaceScope(query, res, "live.stream-v2.validation", headerScope))
|
|
690
|
+
return;
|
|
691
|
+
const streamHeaders = {
|
|
692
|
+
...deps.securityHeaders,
|
|
693
|
+
...deps.corsHeaders,
|
|
694
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
695
|
+
"Cache-Control": "no-cache, no-transform",
|
|
696
|
+
Connection: "keep-alive",
|
|
697
|
+
"X-Accel-Buffering": "no",
|
|
698
|
+
};
|
|
699
|
+
res.writeHead(200, streamHeaders);
|
|
700
|
+
if (typeof res.write !== "function") {
|
|
701
|
+
res.end();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
let closed = false;
|
|
705
|
+
let lastGeneration = -1;
|
|
706
|
+
let refreshTimer = null;
|
|
707
|
+
let keepaliveTimer = null;
|
|
708
|
+
const cleanup = () => {
|
|
709
|
+
if (closed)
|
|
710
|
+
return;
|
|
711
|
+
closed = true;
|
|
712
|
+
if (refreshTimer) {
|
|
713
|
+
clearInterval(refreshTimer);
|
|
714
|
+
refreshTimer = null;
|
|
715
|
+
}
|
|
716
|
+
if (keepaliveTimer) {
|
|
717
|
+
clearInterval(keepaliveTimer);
|
|
718
|
+
keepaliveTimer = null;
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
if (!res.writableEnded)
|
|
722
|
+
res.end();
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
// ignore
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
req.on?.("close", cleanup);
|
|
729
|
+
res.on?.("close", cleanup);
|
|
730
|
+
res.once?.("finish", cleanup);
|
|
731
|
+
const emitSnapshot = async () => {
|
|
732
|
+
if (closed)
|
|
733
|
+
return;
|
|
734
|
+
try {
|
|
735
|
+
const payload = await getSnapshotPayload("v2", path, query, headerScope);
|
|
736
|
+
if (closed)
|
|
737
|
+
return;
|
|
738
|
+
lastGeneration = deps.getSnapshotCacheGeneration();
|
|
739
|
+
writeSseEvent(res, "snapshot", payload);
|
|
740
|
+
}
|
|
741
|
+
catch (err) {
|
|
742
|
+
writeSseEvent(res, "error", {
|
|
743
|
+
error: deps.safeErrorMessage(err),
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
await emitSnapshot();
|
|
748
|
+
refreshTimer = setInterval(() => {
|
|
749
|
+
if (closed) {
|
|
750
|
+
cleanup();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const generation = deps.getSnapshotCacheGeneration();
|
|
754
|
+
if (generation === lastGeneration)
|
|
755
|
+
return;
|
|
756
|
+
void emitSnapshot();
|
|
757
|
+
}, LIVE_STREAM_REFRESH_INTERVAL_MS);
|
|
758
|
+
refreshTimer.unref?.();
|
|
759
|
+
keepaliveTimer = setInterval(() => {
|
|
760
|
+
if (closed || typeof res.write !== "function") {
|
|
761
|
+
cleanup();
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
try {
|
|
765
|
+
res.write(`: ping ${Date.now()}\n\n`);
|
|
766
|
+
}
|
|
767
|
+
catch {
|
|
768
|
+
cleanup();
|
|
769
|
+
}
|
|
770
|
+
}, LIVE_STREAM_KEEPALIVE_MS);
|
|
771
|
+
keepaliveTimer.unref?.();
|
|
772
|
+
}
|
|
631
773
|
async function renderSliceNarrative(path, query, res, headerScope) {
|
|
632
774
|
if (!validateWorkspaceScope(query, res, "live.snapshot.slice-narrative.validation", headerScope))
|
|
633
775
|
return;
|
|
@@ -914,6 +1056,8 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
914
1056
|
router.add("HEAD", "live/snapshot", async ({ path, query, res, req }) => renderSnapshot(path, query, res, headerScopeFromRequest(req)), "Live snapshot (HEAD)");
|
|
915
1057
|
router.add("GET", "live/snapshot-v2", async ({ path, query, res, req }) => renderSnapshotV2(path, query, res, headerScopeFromRequest(req)), "Live snapshot (v2 projections)");
|
|
916
1058
|
router.add("HEAD", "live/snapshot-v2", async ({ path, query, res, req }) => renderSnapshotV2(path, query, res, headerScopeFromRequest(req)), "Live snapshot (v2 projections, HEAD)");
|
|
1059
|
+
router.add("GET", "live/stream-v2", async ({ path, query, req, res }) => renderSnapshotStreamV2(path, query, req, res, headerScopeFromRequest(req)), "Live snapshot stream (v2)");
|
|
1060
|
+
router.add("HEAD", "live/stream-v2", async ({ path, query, req, res }) => renderSnapshotV2(path, query, res, headerScopeFromRequest(req)), "Live snapshot stream (v2, HEAD)");
|
|
917
1061
|
router.add("GET", "slices/*", async ({ path, query, res, req }) => renderSliceNarrative(path, query, res, headerScopeFromRequest(req)), "Slice narrative/timeline projections");
|
|
918
1062
|
router.add("HEAD", "slices/*", async ({ path, query, res, req }) => renderSliceNarrative(path, query, res, headerScopeFromRequest(req)), "Slice narrative/timeline projections (HEAD)");
|
|
919
1063
|
router.add("POST", "slices/*", async ({ path, query, req, res }) => executeSliceAction(path, query, req, res, headerScopeFromRequest(req)), "Slice action contracts");
|
|
@@ -399,6 +399,19 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
399
399
|
: null;
|
|
400
400
|
const maxParallelSlices = normalizeMaxParallelSlices(requestedMaxParallelSlicesRaw, queuePreferredParallel ?? 1);
|
|
401
401
|
const parallelMode = normalizeParallelMode(requestedParallelModeRaw);
|
|
402
|
+
const requestedWorkspaceId = deps.pickString(payload, [
|
|
403
|
+
"workspaceId",
|
|
404
|
+
"workspace_id",
|
|
405
|
+
"command_center_id",
|
|
406
|
+
"projectId",
|
|
407
|
+
"project_id",
|
|
408
|
+
]) ??
|
|
409
|
+
query.get("workspaceId") ??
|
|
410
|
+
query.get("workspace_id") ??
|
|
411
|
+
query.get("command_center_id") ??
|
|
412
|
+
query.get("projectId") ??
|
|
413
|
+
query.get("project_id") ??
|
|
414
|
+
null;
|
|
402
415
|
const existingRun = deps.autoContinueRuns.get(initiativeId) ?? null;
|
|
403
416
|
const existingActiveRunIds = Array.isArray(existingRun?.activeSliceRunIds)
|
|
404
417
|
? (existingRun?.activeSliceRunIds)
|
|
@@ -429,6 +442,7 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
429
442
|
}
|
|
430
443
|
const run = await deps.startAutoContinueRun({
|
|
431
444
|
initiativeId,
|
|
445
|
+
workspaceId: requestedWorkspaceId,
|
|
432
446
|
agentId,
|
|
433
447
|
agentName: requestedAgentName,
|
|
434
448
|
tokenBudget,
|
|
@@ -1566,8 +1580,22 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
1566
1580
|
const startScope = startScopeRaw === "milestone" || startScopeRaw === "workstream"
|
|
1567
1581
|
? startScopeRaw
|
|
1568
1582
|
: "task";
|
|
1583
|
+
const requestedWorkspaceId = deps.pickString(payload, [
|
|
1584
|
+
"workspaceId",
|
|
1585
|
+
"workspace_id",
|
|
1586
|
+
"command_center_id",
|
|
1587
|
+
"projectId",
|
|
1588
|
+
"project_id",
|
|
1589
|
+
]) ??
|
|
1590
|
+
query.get("workspaceId") ??
|
|
1591
|
+
query.get("workspace_id") ??
|
|
1592
|
+
query.get("command_center_id") ??
|
|
1593
|
+
query.get("projectId") ??
|
|
1594
|
+
query.get("project_id") ??
|
|
1595
|
+
null;
|
|
1569
1596
|
const run = await deps.startAutoContinueRun({
|
|
1570
1597
|
initiativeId,
|
|
1598
|
+
workspaceId: requestedWorkspaceId,
|
|
1571
1599
|
agentId,
|
|
1572
1600
|
agentName: await deps.resolveAgentDisplayName(agentId, null),
|
|
1573
1601
|
tokenBudget,
|
|
@@ -38,6 +38,7 @@ type NextUpQueueItem = {
|
|
|
38
38
|
sliceTaskIds?: string[];
|
|
39
39
|
sliceTaskCount?: number | null;
|
|
40
40
|
sliceMilestoneId?: string | null;
|
|
41
|
+
milestoneBreakdown?: MilestoneBreakdownEntry[];
|
|
41
42
|
isPinned?: boolean;
|
|
42
43
|
pinnedRank?: number | null;
|
|
43
44
|
compositeScore?: number;
|
|
@@ -55,9 +56,26 @@ type SliceRunnerAgent = {
|
|
|
55
56
|
type NextUpQueue = {
|
|
56
57
|
items: NextUpQueueItem[];
|
|
57
58
|
degraded: string[];
|
|
59
|
+
summary?: {
|
|
60
|
+
visibleTotal: number;
|
|
61
|
+
stateCounts: Record<NextUpQueueItem["queueState"], number>;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
type MilestoneBreakdownTask = {
|
|
65
|
+
id: string;
|
|
66
|
+
title: string;
|
|
67
|
+
status: string;
|
|
68
|
+
};
|
|
69
|
+
type MilestoneBreakdownEntry = {
|
|
70
|
+
id: string;
|
|
71
|
+
title: string;
|
|
72
|
+
tasks: MilestoneBreakdownTask[];
|
|
73
|
+
totalTasks: number;
|
|
74
|
+
doneTasks: number;
|
|
58
75
|
};
|
|
59
76
|
type RegisterMissionControlReadRoutesDeps<TRes> = {
|
|
60
77
|
autoContinueRuns: Map<string, AutoContinueRunRecord>;
|
|
78
|
+
restoreAutoContinueRun?: (initiativeId: string) => Promise<AutoContinueRunRecord | null>;
|
|
61
79
|
defaultAutoContinueTokenBudget: () => number | null;
|
|
62
80
|
defaultAutoContinueMaxParallelSlices?: () => number;
|
|
63
81
|
autoContinueTickMs: number;
|