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.
Files changed (44) hide show
  1. package/README.md +43 -24
  2. package/docs/CHANGELOG.md +69 -0
  3. package/frontend/openapi.json +716 -3
  4. package/frontend/src/api/client.ts +119 -2
  5. package/frontend/src/api/openapi.ts +621 -4
  6. package/frontend/src/components/FirstRunGuide.tsx +3 -3
  7. package/frontend/src/features/review/ReviewCard.tsx +91 -0
  8. package/frontend/src/features/review/ReviewInbox.tsx +112 -0
  9. package/frontend/src/features/review/reviewHelpers.ts +69 -0
  10. package/frontend/src/i18n.ts +8 -8
  11. package/frontend/src/pages/Act.tsx +28 -3
  12. package/frontend/src/routes.ts +2 -0
  13. package/lattice_brain/__init__.py +1 -1
  14. package/lattice_brain/runtime/multi_agent.py +1 -1
  15. package/latticeai/__init__.py +1 -1
  16. package/latticeai/api/review_queue.py +162 -0
  17. package/latticeai/app_factory.py +235 -456
  18. package/latticeai/core/marketplace.py +1 -1
  19. package/latticeai/core/workspace_os.py +86 -1
  20. package/latticeai/runtime/app_context_runtime.py +13 -0
  21. package/latticeai/runtime/automation_runtime.py +64 -0
  22. package/latticeai/runtime/bootstrap.py +48 -0
  23. package/latticeai/runtime/context_runtime.py +43 -0
  24. package/latticeai/runtime/hooks_runtime.py +77 -0
  25. package/latticeai/runtime/lifespan_runtime.py +138 -0
  26. package/latticeai/runtime/persistence_runtime.py +87 -0
  27. package/latticeai/runtime/platform_services_runtime.py +39 -0
  28. package/latticeai/runtime/router_registration.py +570 -0
  29. package/latticeai/runtime/web_runtime.py +65 -0
  30. package/latticeai/services/review_queue.py +271 -0
  31. package/latticeai/services/run_executor.py +33 -0
  32. package/latticeai/services/triggers.py +30 -1
  33. package/package.json +1 -1
  34. package/src-tauri/Cargo.lock +1 -1
  35. package/src-tauri/Cargo.toml +1 -1
  36. package/src-tauri/tauri.conf.json +1 -1
  37. package/static/app/asset-manifest.json +5 -5
  38. package/static/app/assets/index-D2zafMYb.js +16 -0
  39. package/static/app/assets/index-D2zafMYb.js.map +1 -0
  40. package/static/app/assets/index-xRn29gI8.css +2 -0
  41. package/static/app/index.html +2 -2
  42. package/static/app/assets/index-C7vzwUjU.js +0 -16
  43. package/static/app/assets/index-C7vzwUjU.js.map +0 -1
  44. 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
- const clients = new Map<string, ReturnType<typeof createClient<paths>>>();
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)}`, {}, {}),