ltcai 5.5.0 → 6.0.0
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 +43 -24
- package/docs/CHANGELOG.md +69 -0
- package/frontend/openapi.json +716 -3
- package/frontend/src/api/client.ts +119 -2
- package/frontend/src/api/openapi.ts +621 -4
- package/frontend/src/components/FirstRunGuide.tsx +3 -3
- package/frontend/src/features/review/ReviewCard.tsx +91 -0
- package/frontend/src/features/review/ReviewInbox.tsx +112 -0
- package/frontend/src/features/review/reviewHelpers.ts +69 -0
- package/frontend/src/i18n.ts +8 -8
- package/frontend/src/pages/Act.tsx +28 -3
- package/frontend/src/routes.ts +2 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/review_queue.py +162 -0
- package/latticeai/app_factory.py +235 -456
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +86 -1
- package/latticeai/runtime/app_context_runtime.py +13 -0
- package/latticeai/runtime/automation_runtime.py +64 -0
- package/latticeai/runtime/bootstrap.py +48 -0
- package/latticeai/runtime/context_runtime.py +43 -0
- package/latticeai/runtime/hooks_runtime.py +77 -0
- package/latticeai/runtime/lifespan_runtime.py +138 -0
- package/latticeai/runtime/persistence_runtime.py +87 -0
- package/latticeai/runtime/platform_services_runtime.py +39 -0
- package/latticeai/runtime/router_registration.py +570 -0
- package/latticeai/runtime/web_runtime.py +65 -0
- package/latticeai/services/review_queue.py +271 -0
- package/latticeai/services/run_executor.py +33 -0
- package/latticeai/services/triggers.py +30 -1
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +5 -5
- package/static/app/assets/index-D2zafMYb.js +16 -0
- package/static/app/assets/index-D2zafMYb.js.map +1 -0
- package/static/app/assets/index-xRn29gI8.css +2 -0
- package/static/app/index.html +2 -2
- package/static/app/assets/index-C7vzwUjU.js +0 -16
- package/static/app/assets/index-C7vzwUjU.js.map +0 -1
- package/static/app/assets/index-HN4f2wbe.css +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import createClient from "openapi-fetch";
|
|
2
|
-
import type { paths } from "./openapi";
|
|
2
|
+
import type { components, operations, paths } from "./openapi";
|
|
3
3
|
import { useAppStore } from "@/store/appStore";
|
|
4
4
|
|
|
5
5
|
export type ApiResult<T = unknown> = {
|
|
@@ -21,7 +21,19 @@ export type AdminAuditFilters = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const TIMEOUT_MS = 10_000;
|
|
24
|
-
|
|
24
|
+
type OpenApiClient = ReturnType<typeof createClient<paths>>;
|
|
25
|
+
type OperationJson200<Operation> = Operation extends {
|
|
26
|
+
responses: { 200: { content: { "application/json": infer Result } } };
|
|
27
|
+
} ? Result : never;
|
|
28
|
+
type ReviewListOperation = operations["list_items_automation_reviews_get"];
|
|
29
|
+
type ReviewItemOperation =
|
|
30
|
+
| operations["approve_item_automation_reviews__item_id__approve_post"]
|
|
31
|
+
| operations["dismiss_item_automation_reviews__item_id__dismiss_post"]
|
|
32
|
+
| operations["run_now_item_automation_reviews__item_id__run_now_post"]
|
|
33
|
+
| operations["snooze_item_automation_reviews__item_id__snooze_post"]
|
|
34
|
+
| operations["unsnooze_item_automation_reviews__item_id__unsnooze_post"];
|
|
35
|
+
|
|
36
|
+
const clients = new Map<string, OpenApiClient>();
|
|
25
37
|
let desktopBase: Promise<string | null> | null = null;
|
|
26
38
|
|
|
27
39
|
declare global {
|
|
@@ -145,6 +157,39 @@ async function apiJson<T>(
|
|
|
145
157
|
}
|
|
146
158
|
}
|
|
147
159
|
|
|
160
|
+
async function openApiJson<T>(
|
|
161
|
+
shape: T,
|
|
162
|
+
execute: (client: OpenApiClient, signal: AbortSignal) => Promise<{ data?: T; error?: unknown; response: Response }>,
|
|
163
|
+
): Promise<ApiResult<T>> {
|
|
164
|
+
const base = await apiBase();
|
|
165
|
+
const client = clientFor(base);
|
|
166
|
+
const ctrl = new AbortController();
|
|
167
|
+
const timer = window.setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
168
|
+
try {
|
|
169
|
+
const { data, error, response } = await execute(client, ctrl.signal);
|
|
170
|
+
if (response.ok && data !== undefined) {
|
|
171
|
+
return { ok: true, status: response.status, data, source: "live" };
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
ok: false,
|
|
175
|
+
status: response.status,
|
|
176
|
+
data: emptyFor(shape),
|
|
177
|
+
source: "unavailable",
|
|
178
|
+
error: friendlyError(error, response.statusText),
|
|
179
|
+
};
|
|
180
|
+
} catch (err) {
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
status: 0,
|
|
184
|
+
data: emptyFor(shape),
|
|
185
|
+
source: "unavailable",
|
|
186
|
+
error: err instanceof Error ? err.message : String(err),
|
|
187
|
+
};
|
|
188
|
+
} finally {
|
|
189
|
+
window.clearTimeout(timer);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
148
193
|
function get<T>(path: string, shape: T, query?: Query) {
|
|
149
194
|
return apiJson<T>("GET", path, { query, shape });
|
|
150
195
|
}
|
|
@@ -161,6 +206,72 @@ function del<T>(path: string, shape: T) {
|
|
|
161
206
|
return apiJson<T>("DELETE", path, { shape });
|
|
162
207
|
}
|
|
163
208
|
|
|
209
|
+
export type ReviewItem = components["schemas"]["ReviewItem"];
|
|
210
|
+
export type ReviewItemList = components["schemas"]["ReviewItemList"];
|
|
211
|
+
export type ReviewStatusFilter = "pending" | "snoozed" | "approved" | "dismissed" | "all";
|
|
212
|
+
export type ReviewSourceFilter = "workflow_run" | "trigger" | "kg_change_digest" | "all";
|
|
213
|
+
|
|
214
|
+
function reviewItemShape(): ReviewItem {
|
|
215
|
+
return {
|
|
216
|
+
id: "",
|
|
217
|
+
status: "pending",
|
|
218
|
+
effective_status: "pending",
|
|
219
|
+
title: "",
|
|
220
|
+
summary: "",
|
|
221
|
+
source: "workflow_run",
|
|
222
|
+
kind: "suggestion",
|
|
223
|
+
payload: {},
|
|
224
|
+
provenance: {},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function reviewItemListShape(): ReviewItemList {
|
|
229
|
+
return { items: [] };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function reviewList(query?: { status?: Exclude<ReviewStatusFilter, "all">; source?: Exclude<ReviewSourceFilter, "all"> }) {
|
|
233
|
+
return openApiJson<OperationJson200<ReviewListOperation>>(
|
|
234
|
+
reviewItemListShape(),
|
|
235
|
+
(client, signal) => client.GET("/automation/reviews", {
|
|
236
|
+
params: { query: query || {} },
|
|
237
|
+
headers: workspaceHeaders(),
|
|
238
|
+
signal,
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function reviewAction(
|
|
244
|
+
id: string,
|
|
245
|
+
action: "approve" | "dismiss" | "run_now" | "unsnooze",
|
|
246
|
+
) {
|
|
247
|
+
return openApiJson<OperationJson200<ReviewItemOperation>>(
|
|
248
|
+
reviewItemShape(),
|
|
249
|
+
(client, signal) => {
|
|
250
|
+
const request = {
|
|
251
|
+
params: { path: { item_id: id } },
|
|
252
|
+
headers: workspaceHeaders(),
|
|
253
|
+
signal,
|
|
254
|
+
};
|
|
255
|
+
if (action === "approve") return client.POST("/automation/reviews/{item_id}/approve", request);
|
|
256
|
+
if (action === "dismiss") return client.POST("/automation/reviews/{item_id}/dismiss", request);
|
|
257
|
+
if (action === "run_now") return client.POST("/automation/reviews/{item_id}/run_now", request);
|
|
258
|
+
return client.POST("/automation/reviews/{item_id}/unsnooze", request);
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function snoozeReview(id: string, until: string) {
|
|
264
|
+
return openApiJson<OperationJson200<operations["snooze_item_automation_reviews__item_id__snooze_post"]>>(
|
|
265
|
+
reviewItemShape(),
|
|
266
|
+
(client, signal) => client.POST("/automation/reviews/{item_id}/snooze", {
|
|
267
|
+
params: { path: { item_id: id } },
|
|
268
|
+
body: { until },
|
|
269
|
+
headers: workspaceHeaders(),
|
|
270
|
+
signal,
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
164
275
|
async function uploadDocument(file: File): Promise<ApiResult<Record<string, unknown> | null>> {
|
|
165
276
|
const base = await apiBase();
|
|
166
277
|
const form = new FormData();
|
|
@@ -380,6 +491,12 @@ export const latticeApi = {
|
|
|
380
491
|
hooks: () => get("/api/hooks", { hooks: [] }),
|
|
381
492
|
hookRuns: () => get("/api/hooks/runs", { runs: [] }, { limit: 50 }),
|
|
382
493
|
hookRun: (body: unknown) => post("/api/hooks/run", body, {}),
|
|
494
|
+
automationReviews: reviewList,
|
|
495
|
+
approveReviewItem: (id: string) => reviewAction(id, "approve"),
|
|
496
|
+
dismissReviewItem: (id: string) => reviewAction(id, "dismiss"),
|
|
497
|
+
snoozeReviewItem: snoozeReview,
|
|
498
|
+
unsnoozeReviewItem: (id: string) => reviewAction(id, "unsnooze"),
|
|
499
|
+
runNowReviewItem: (id: string) => reviewAction(id, "run_now"),
|
|
383
500
|
permissionsPending: () => get("/permissions/pending", { pending: {}, count: 0 }),
|
|
384
501
|
approvePermission: (token: string) => post(`/permissions/approve/${encodeURIComponent(token)}`, {}, {}),
|
|
385
502
|
denyPermission: (token: string) => post(`/permissions/deny/${encodeURIComponent(token)}`, {}, {}),
|