ltcai 7.9.0 → 8.1.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 +35 -49
- package/docs/CHANGELOG.md +35 -0
- package/docs/V3_FRONTEND.md +11 -5
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +9 -1
- package/lattice_brain/graph/projection.py +19 -5
- package/lattice_brain/graph/provenance.py +26 -4
- package/lattice_brain/graph/write_master.py +30 -3
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/app_factory.py +13 -134
- package/latticeai/core/local_embeddings.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/mcp_registry.py +140 -1
- package/latticeai/core/tool_registry.py +7 -0
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/architecture_readiness.py +50 -6
- package/latticeai/services/model_runtime.py +36 -2
- package/latticeai/services/product_readiness.py +19 -17
- package/package.json +2 -2
- package/scripts/build_frontend_assets.mjs +11 -2
- 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 +11 -102
- package/static/app/assets/Act-BOO66G-c.js +1 -0
- package/static/app/assets/Brain-C6lEYiD7.js +321 -0
- package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-TATXBRDw.js} +1 -2
- package/static/app/assets/Library-DK4FIp8a.js +1 -0
- package/static/app/assets/System-Bgs6Ql7x.js +1 -0
- package/static/app/assets/core-CwxXejkd.js +1 -2
- package/static/app/assets/index-Bvv79nre.js +16 -0
- package/static/app/assets/index-Dslqglia.css +2 -0
- package/static/app/assets/primitives-B70WOra0.js +1 -0
- package/static/app/assets/{textarea-BZk6ybp5.js → textarea-Czrd9gwM.js} +1 -2
- package/static/app/index.html +3 -10
- package/static/app/theme-boot.js +8 -0
- package/static/sw.js +1 -1
- package/frontend/index.html +0 -24
- package/frontend/openapi.json +0 -15425
- package/frontend/src/App.tsx +0 -243
- package/frontend/src/api/client.ts +0 -580
- package/frontend/src/api/openapi.ts +0 -17892
- package/frontend/src/components/AdminAccessGate.tsx +0 -70
- package/frontend/src/components/FeedbackState.tsx +0 -45
- package/frontend/src/components/LanguageSwitcher.tsx +0 -23
- package/frontend/src/components/LivingBrain.tsx +0 -220
- package/frontend/src/components/ProductFlow.tsx +0 -171
- package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
- package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
- package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
- package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
- package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
- package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
- package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
- package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
- package/frontend/src/components/primitives.tsx +0 -392
- package/frontend/src/components/ui/badge.tsx +0 -27
- package/frontend/src/components/ui/button.tsx +0 -37
- package/frontend/src/components/ui/card.tsx +0 -22
- package/frontend/src/components/ui/input.tsx +0 -16
- package/frontend/src/components/ui/textarea.tsx +0 -16
- package/frontend/src/features/admin/AdminConsole.tsx +0 -334
- package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
- package/frontend/src/features/brain/BrainComposer.tsx +0 -85
- package/frontend/src/features/brain/BrainConversation.tsx +0 -688
- package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
- package/frontend/src/features/brain/BrainHome.tsx +0 -624
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
- package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
- package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
- package/frontend/src/features/brain/brainData.ts +0 -246
- package/frontend/src/features/brain/graphLayout.ts +0 -37
- package/frontend/src/features/brain/types.ts +0 -150
- package/frontend/src/features/review/ReviewCard.tsx +0 -100
- package/frontend/src/features/review/ReviewInbox.tsx +0 -127
- package/frontend/src/features/review/reviewHelpers.ts +0 -69
- package/frontend/src/i18n.ts +0 -1303
- package/frontend/src/lib/utils.ts +0 -33
- package/frontend/src/main.tsx +0 -23
- package/frontend/src/pages/Act.tsx +0 -458
- package/frontend/src/pages/Ask.tsx +0 -14
- package/frontend/src/pages/Brain.tsx +0 -914
- package/frontend/src/pages/Capture.tsx +0 -258
- package/frontend/src/pages/Library.tsx +0 -486
- package/frontend/src/pages/System.tsx +0 -621
- package/frontend/src/routes.ts +0 -92
- package/frontend/src/store/appStore.ts +0 -94
- package/frontend/src/styles.css +0 -6579
- package/static/app/assets/Act-DOvf59ru.js +0 -2
- package/static/app/assets/Act-DOvf59ru.js.map +0 -1
- package/static/app/assets/Brain-C7_0mEiI.js +0 -322
- package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
- package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
- package/static/app/assets/Library-BJPEEm5O.js +0 -2
- package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
- package/static/app/assets/System-D6t9jo9V.js +0 -2
- package/static/app/assets/System-D6t9jo9V.js.map +0 -1
- package/static/app/assets/core-CwxXejkd.js.map +0 -1
- package/static/app/assets/index-C7g26IF6.css +0 -2
- package/static/app/assets/index-DbcEYJQ2.js +0 -17
- package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
- package/static/app/assets/primitives-CD38lt4n.js +0 -2
- package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
- package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
|
@@ -1,580 +0,0 @@
|
|
|
1
|
-
import createClient from "openapi-fetch";
|
|
2
|
-
import type { components, operations, paths } from "./openapi";
|
|
3
|
-
import { useAppStore } from "@/store/appStore";
|
|
4
|
-
|
|
5
|
-
export type ApiResult<T = unknown> = {
|
|
6
|
-
ok: boolean;
|
|
7
|
-
status: number;
|
|
8
|
-
data: T;
|
|
9
|
-
source: "live" | "unavailable";
|
|
10
|
-
error?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE";
|
|
14
|
-
type Query = Record<string, string | number | boolean | null | undefined>;
|
|
15
|
-
export type AdminAuditFilters = {
|
|
16
|
-
q?: string;
|
|
17
|
-
actor?: string;
|
|
18
|
-
action?: string;
|
|
19
|
-
severity?: string;
|
|
20
|
-
limit?: number;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const TIMEOUT_MS = 10_000;
|
|
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>();
|
|
37
|
-
let desktopBase: Promise<string | null> | null = null;
|
|
38
|
-
|
|
39
|
-
declare global {
|
|
40
|
-
interface Window {
|
|
41
|
-
__TAURI_INTERNALS__?: unknown;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function sameOriginBase() {
|
|
46
|
-
return "";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function tauriBackendOrigin(): Promise<string | null> {
|
|
50
|
-
if (!window.__TAURI_INTERNALS__) return null;
|
|
51
|
-
if (!desktopBase) {
|
|
52
|
-
desktopBase = import("@tauri-apps/api/core")
|
|
53
|
-
.then(({ invoke }) => invoke<string>("backend_origin"))
|
|
54
|
-
.then((origin) => origin || null)
|
|
55
|
-
.catch(() => null);
|
|
56
|
-
}
|
|
57
|
-
return desktopBase;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function tauriInvoke<T>(command: string): Promise<T | null> {
|
|
61
|
-
if (!window.__TAURI_INTERNALS__) return null;
|
|
62
|
-
try {
|
|
63
|
-
const { invoke } = await import("@tauri-apps/api/core");
|
|
64
|
-
return await invoke<T>(command);
|
|
65
|
-
} catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function apiBase() {
|
|
71
|
-
const stateBase = useAppStore.getState().apiBase;
|
|
72
|
-
if (stateBase) return stateBase;
|
|
73
|
-
const desktop = await tauriBackendOrigin();
|
|
74
|
-
if (desktop) {
|
|
75
|
-
useAppStore.getState().setApiBase(desktop);
|
|
76
|
-
return desktop;
|
|
77
|
-
}
|
|
78
|
-
return sameOriginBase();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function clientFor(baseUrl: string) {
|
|
82
|
-
if (!clients.has(baseUrl)) {
|
|
83
|
-
clients.set(baseUrl, createClient<paths>({ baseUrl, credentials: "same-origin" }));
|
|
84
|
-
}
|
|
85
|
-
return clients.get(baseUrl)!;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function emptyFor<T>(shape: T): T {
|
|
89
|
-
if (Array.isArray(shape)) return [] as T;
|
|
90
|
-
if (shape && typeof shape === "object") return { ...(shape as Record<string, unknown>) } as T;
|
|
91
|
-
return shape;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function workspaceHeaders(): Record<string, string> {
|
|
95
|
-
const workspaceId = useAppStore.getState().workspaceId;
|
|
96
|
-
return workspaceId ? { "X-Workspace-Id": workspaceId } : {};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function friendlyError(error: unknown, fallback: string) {
|
|
100
|
-
if (!error) return fallback;
|
|
101
|
-
const record = typeof error === "object" && error !== null ? error as Record<string, unknown> : null;
|
|
102
|
-
const detail = record?.detail;
|
|
103
|
-
if (typeof detail === "string") return detail;
|
|
104
|
-
const detailRecord = typeof detail === "object" && detail !== null ? detail as Record<string, unknown> : null;
|
|
105
|
-
if (detailRecord) {
|
|
106
|
-
const message = detailRecord.user_message || detailRecord.reason || detailRecord.action || detailRecord.status;
|
|
107
|
-
if (message) return String(message);
|
|
108
|
-
}
|
|
109
|
-
const message = record?.message || record?.error;
|
|
110
|
-
if (message) return String(message);
|
|
111
|
-
return fallback;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function friendlyCaughtError(error: unknown, fallback: string) {
|
|
115
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
116
|
-
if (/not valid JSON|Unexpected token|JSON/i.test(message)) return fallback;
|
|
117
|
-
if (/aborted|abort/i.test(message)) return "Request timed out. Check that the local Lattice service is running.";
|
|
118
|
-
return message || fallback;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function apiJson<T>(
|
|
122
|
-
method: HttpMethod,
|
|
123
|
-
path: string,
|
|
124
|
-
opts: { body?: unknown; query?: Query; headers?: Record<string, string>; shape: T },
|
|
125
|
-
): Promise<ApiResult<T>> {
|
|
126
|
-
const base = await apiBase();
|
|
127
|
-
const client = clientFor(base);
|
|
128
|
-
const ctrl = new AbortController();
|
|
129
|
-
const timer = window.setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
130
|
-
try {
|
|
131
|
-
const request = {
|
|
132
|
-
body: opts.body,
|
|
133
|
-
params: { query: opts.query || {} },
|
|
134
|
-
headers: { ...workspaceHeaders(), ...(opts.headers || {}) },
|
|
135
|
-
signal: ctrl.signal,
|
|
136
|
-
} as never;
|
|
137
|
-
const call =
|
|
138
|
-
method === "GET" ? client.GET :
|
|
139
|
-
method === "POST" ? client.POST :
|
|
140
|
-
method === "PATCH" ? client.PATCH :
|
|
141
|
-
client.DELETE;
|
|
142
|
-
const result = await (call as unknown as (p: never, r: never) => Promise<{ data?: unknown; error?: unknown; response: Response }>)(path as never, request);
|
|
143
|
-
const { data, error, response } = result;
|
|
144
|
-
if (response.ok && data !== undefined) {
|
|
145
|
-
return { ok: true, status: response.status, data: data as T, source: "live" };
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
ok: false,
|
|
149
|
-
status: response.status,
|
|
150
|
-
data: emptyFor(opts.shape),
|
|
151
|
-
source: "unavailable",
|
|
152
|
-
error: friendlyError(error, response.statusText),
|
|
153
|
-
};
|
|
154
|
-
} catch (err) {
|
|
155
|
-
return {
|
|
156
|
-
ok: false,
|
|
157
|
-
status: 0,
|
|
158
|
-
data: emptyFor(opts.shape),
|
|
159
|
-
source: "unavailable",
|
|
160
|
-
error: friendlyCaughtError(err, "The local Lattice service returned an unavailable response."),
|
|
161
|
-
};
|
|
162
|
-
} finally {
|
|
163
|
-
window.clearTimeout(timer);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async function openApiJson<T>(
|
|
168
|
-
shape: T,
|
|
169
|
-
execute: (client: OpenApiClient, signal: AbortSignal) => Promise<{ data?: T; error?: unknown; response: Response }>,
|
|
170
|
-
): Promise<ApiResult<T>> {
|
|
171
|
-
const base = await apiBase();
|
|
172
|
-
const client = clientFor(base);
|
|
173
|
-
const ctrl = new AbortController();
|
|
174
|
-
const timer = window.setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
175
|
-
try {
|
|
176
|
-
const { data, error, response } = await execute(client, ctrl.signal);
|
|
177
|
-
if (response.ok && data !== undefined) {
|
|
178
|
-
return { ok: true, status: response.status, data, source: "live" };
|
|
179
|
-
}
|
|
180
|
-
return {
|
|
181
|
-
ok: false,
|
|
182
|
-
status: response.status,
|
|
183
|
-
data: emptyFor(shape),
|
|
184
|
-
source: "unavailable",
|
|
185
|
-
error: friendlyError(error, response.statusText),
|
|
186
|
-
};
|
|
187
|
-
} catch (err) {
|
|
188
|
-
return {
|
|
189
|
-
ok: false,
|
|
190
|
-
status: 0,
|
|
191
|
-
data: emptyFor(shape),
|
|
192
|
-
source: "unavailable",
|
|
193
|
-
error: friendlyCaughtError(err, "The local Lattice service returned an unavailable response."),
|
|
194
|
-
};
|
|
195
|
-
} finally {
|
|
196
|
-
window.clearTimeout(timer);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function get<T>(path: string, shape: T, query?: Query) {
|
|
201
|
-
return apiJson<T>("GET", path, { query, shape });
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function post<T>(path: string, body: unknown, shape: T) {
|
|
205
|
-
return apiJson<T>("POST", path, { body, shape });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function patch<T>(path: string, body: unknown, shape: T) {
|
|
209
|
-
return apiJson<T>("PATCH", path, { body, shape });
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function del<T>(path: string, shape: T) {
|
|
213
|
-
return apiJson<T>("DELETE", path, { shape });
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export type ReviewItem = components["schemas"]["ReviewItem"];
|
|
217
|
-
export type ReviewItemList = components["schemas"]["ReviewItemList"];
|
|
218
|
-
export type ReviewStatusFilter = "pending" | "snoozed" | "approved" | "dismissed" | "all";
|
|
219
|
-
export type ReviewSourceFilter = "workflow_run" | "trigger" | "kg_change_digest" | "all";
|
|
220
|
-
|
|
221
|
-
function reviewItemShape(): ReviewItem {
|
|
222
|
-
return {
|
|
223
|
-
id: "",
|
|
224
|
-
status: "pending",
|
|
225
|
-
effective_status: "pending",
|
|
226
|
-
title: "",
|
|
227
|
-
summary: "",
|
|
228
|
-
source: "workflow_run",
|
|
229
|
-
kind: "suggestion",
|
|
230
|
-
payload: {},
|
|
231
|
-
provenance: {},
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function reviewItemListShape(): ReviewItemList {
|
|
236
|
-
return { items: [] };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function reviewList(query?: { status?: Exclude<ReviewStatusFilter, "all">; source?: Exclude<ReviewSourceFilter, "all"> }) {
|
|
240
|
-
return openApiJson<OperationJson200<ReviewListOperation>>(
|
|
241
|
-
reviewItemListShape(),
|
|
242
|
-
(client, signal) => client.GET("/automation/reviews", {
|
|
243
|
-
params: { query: query || {} },
|
|
244
|
-
headers: workspaceHeaders(),
|
|
245
|
-
signal,
|
|
246
|
-
}),
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function reviewAction(
|
|
251
|
-
id: string,
|
|
252
|
-
action: "approve" | "dismiss" | "run_now" | "unsnooze",
|
|
253
|
-
) {
|
|
254
|
-
return openApiJson<OperationJson200<ReviewItemOperation>>(
|
|
255
|
-
reviewItemShape(),
|
|
256
|
-
(client, signal) => {
|
|
257
|
-
const request = {
|
|
258
|
-
params: { path: { item_id: id } },
|
|
259
|
-
headers: workspaceHeaders(),
|
|
260
|
-
signal,
|
|
261
|
-
};
|
|
262
|
-
if (action === "approve") return client.POST("/automation/reviews/{item_id}/approve", request);
|
|
263
|
-
if (action === "dismiss") return client.POST("/automation/reviews/{item_id}/dismiss", request);
|
|
264
|
-
if (action === "run_now") return client.POST("/automation/reviews/{item_id}/run_now", request);
|
|
265
|
-
return client.POST("/automation/reviews/{item_id}/unsnooze", request);
|
|
266
|
-
},
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function snoozeReview(id: string, until: string) {
|
|
271
|
-
return openApiJson<OperationJson200<operations["snooze_item_automation_reviews__item_id__snooze_post"]>>(
|
|
272
|
-
reviewItemShape(),
|
|
273
|
-
(client, signal) => client.POST("/automation/reviews/{item_id}/snooze", {
|
|
274
|
-
params: { path: { item_id: id } },
|
|
275
|
-
body: { until },
|
|
276
|
-
headers: workspaceHeaders(),
|
|
277
|
-
signal,
|
|
278
|
-
}),
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function uploadDocument(file: File): Promise<ApiResult<Record<string, unknown> | null>> {
|
|
283
|
-
const base = await apiBase();
|
|
284
|
-
const form = new FormData();
|
|
285
|
-
form.append("file", file);
|
|
286
|
-
try {
|
|
287
|
-
const res = await fetch(`${base}/upload/document`, {
|
|
288
|
-
method: "POST",
|
|
289
|
-
credentials: "same-origin",
|
|
290
|
-
headers: { Accept: "application/json", ...workspaceHeaders() } satisfies HeadersInit,
|
|
291
|
-
body: form,
|
|
292
|
-
});
|
|
293
|
-
const data = await res.json().catch(() => null);
|
|
294
|
-
return { ok: res.ok, status: res.status, data, source: res.ok ? "live" : "unavailable" };
|
|
295
|
-
} catch (err) {
|
|
296
|
-
return { ok: false, status: 0, data: null, source: "unavailable", error: String(err) };
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export type ChatEventHandlers = {
|
|
301
|
-
onChunk?: (delta: string, fullText: string) => void;
|
|
302
|
-
onTrace?: (trace: unknown) => void;
|
|
303
|
-
signal?: AbortSignal;
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
export type ModelPrepareHandlers = {
|
|
307
|
-
onProgress?: (data: Record<string, unknown>) => void;
|
|
308
|
-
onDone?: (data: Record<string, unknown>) => void;
|
|
309
|
-
onError?: (data: Record<string, unknown>) => void;
|
|
310
|
-
signal?: AbortSignal;
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
async function streamModelPrepare(
|
|
314
|
-
body: { model: string; engine?: string; allow_download?: boolean },
|
|
315
|
-
handlers: ModelPrepareHandlers = {},
|
|
316
|
-
) {
|
|
317
|
-
const base = await apiBase();
|
|
318
|
-
const res = await fetch(`${base}/engines/prepare-model/stream`, {
|
|
319
|
-
method: "POST",
|
|
320
|
-
credentials: "same-origin",
|
|
321
|
-
signal: handlers.signal,
|
|
322
|
-
headers: {
|
|
323
|
-
"Content-Type": "application/json",
|
|
324
|
-
Accept: "text/event-stream",
|
|
325
|
-
...workspaceHeaders(),
|
|
326
|
-
} satisfies HeadersInit,
|
|
327
|
-
body: JSON.stringify({ engine: null, allow_download: false, ...body }),
|
|
328
|
-
});
|
|
329
|
-
if (!res.ok || !res.body || !(res.headers.get("content-type") || "").includes("text/event-stream")) {
|
|
330
|
-
const payload = await res.json().catch(() => null);
|
|
331
|
-
const detail = payload?.detail && typeof payload.detail === "object" ? payload.detail : payload;
|
|
332
|
-
const message = friendlyError(payload, res.statusText);
|
|
333
|
-
handlers.onError?.({ status: "error", user_message: message, ...(detail || {}) });
|
|
334
|
-
return { source: "live" as const, ok: false, status: res.status, data: detail || {}, error: message };
|
|
335
|
-
}
|
|
336
|
-
const reader = res.body.getReader();
|
|
337
|
-
const decoder = new TextDecoder();
|
|
338
|
-
let buffer = "";
|
|
339
|
-
let eventName = "message";
|
|
340
|
-
let finalData: Record<string, unknown> = {};
|
|
341
|
-
for (;;) {
|
|
342
|
-
const { done, value } = await reader.read();
|
|
343
|
-
if (done) break;
|
|
344
|
-
buffer += decoder.decode(value, { stream: true });
|
|
345
|
-
const parts = buffer.split("\n\n");
|
|
346
|
-
buffer = parts.pop() || "";
|
|
347
|
-
for (const part of parts) {
|
|
348
|
-
const lines = part.split("\n");
|
|
349
|
-
eventName = lines.find((item) => item.startsWith("event:"))?.slice(6).trim() || "message";
|
|
350
|
-
const dataLine = lines.find((item) => item.startsWith("data:"));
|
|
351
|
-
if (!dataLine) continue;
|
|
352
|
-
const raw = dataLine.slice(5).trim();
|
|
353
|
-
const data = raw ? JSON.parse(raw) as Record<string, unknown> : {};
|
|
354
|
-
if (eventName === "progress") handlers.onProgress?.(data);
|
|
355
|
-
if (eventName === "error") {
|
|
356
|
-
const detail = typeof data.detail === "object" && data.detail !== null ? data.detail as Record<string, unknown> : data;
|
|
357
|
-
handlers.onError?.(detail);
|
|
358
|
-
return { source: "live" as const, ok: false, status: Number(data.status_code || 500), data: detail, error: friendlyError({ detail }, "Model setup failed") };
|
|
359
|
-
}
|
|
360
|
-
if (eventName === "done") {
|
|
361
|
-
finalData = data;
|
|
362
|
-
handlers.onDone?.(data);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return { source: "live" as const, ok: true, status: 200, data: finalData };
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
async function streamChat(body: Record<string, unknown>, handlers: ChatEventHandlers = {}) {
|
|
370
|
-
const base = await apiBase();
|
|
371
|
-
const res = await fetch(`${base}/chat`, {
|
|
372
|
-
method: "POST",
|
|
373
|
-
credentials: "same-origin",
|
|
374
|
-
signal: handlers.signal,
|
|
375
|
-
headers: {
|
|
376
|
-
"Content-Type": "application/json",
|
|
377
|
-
Accept: "text/event-stream",
|
|
378
|
-
...workspaceHeaders(),
|
|
379
|
-
} satisfies HeadersInit,
|
|
380
|
-
body: JSON.stringify({ stream: true, max_tokens: 2048, temperature: 0.2, ...body }),
|
|
381
|
-
});
|
|
382
|
-
if (!res.ok || !res.body || !(res.headers.get("content-type") || "").includes("text/event-stream")) {
|
|
383
|
-
const payload = await res.json().catch(() => null);
|
|
384
|
-
return { source: "live", text: "", trace: null, error: payload?.error || payload?.detail || res.statusText };
|
|
385
|
-
}
|
|
386
|
-
const reader = res.body.getReader();
|
|
387
|
-
const decoder = new TextDecoder();
|
|
388
|
-
let buffer = "";
|
|
389
|
-
let text = "";
|
|
390
|
-
let trace: unknown = null;
|
|
391
|
-
for (;;) {
|
|
392
|
-
const { done, value } = await reader.read();
|
|
393
|
-
if (done) break;
|
|
394
|
-
buffer += decoder.decode(value, { stream: true });
|
|
395
|
-
const parts = buffer.split("\n\n");
|
|
396
|
-
buffer = parts.pop() || "";
|
|
397
|
-
for (const part of parts) {
|
|
398
|
-
const line = part.split("\n").find((item) => item.startsWith("data:"));
|
|
399
|
-
if (!line) continue;
|
|
400
|
-
const raw = line.slice(5).trim();
|
|
401
|
-
if (raw === "[DONE]") return { source: "live", text, trace };
|
|
402
|
-
const data = JSON.parse(raw);
|
|
403
|
-
const delta = data.chunk || data.text || "";
|
|
404
|
-
if (delta) {
|
|
405
|
-
text += delta;
|
|
406
|
-
handlers.onChunk?.(delta, text);
|
|
407
|
-
}
|
|
408
|
-
if (data.trace) {
|
|
409
|
-
trace = data.trace;
|
|
410
|
-
handlers.onTrace?.(trace);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return { source: "live", text, trace };
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
export const latticeApi = {
|
|
418
|
-
raw: get,
|
|
419
|
-
desktopBackendStatus: async (): Promise<ApiResult<Record<string, unknown>>> => {
|
|
420
|
-
const status = await tauriInvoke<Record<string, unknown>>("backend_status");
|
|
421
|
-
if (status) return { ok: true, status: 200, data: status, source: "live" };
|
|
422
|
-
return { ok: false, status: 0, data: {}, source: "unavailable", error: "Desktop backend status is available only inside the Tauri shell." };
|
|
423
|
-
},
|
|
424
|
-
health: () => get("/health", {}),
|
|
425
|
-
workspaceOs: () => get("/workspace/os", { counts: {}, models: {}, workspace_registry: { workspaces: [] } }),
|
|
426
|
-
workspaceVscodeStatus: () => get("/workspace/vscode/status", {
|
|
427
|
-
connected: false,
|
|
428
|
-
last_seen_ms: 0,
|
|
429
|
-
status: "offline",
|
|
430
|
-
index_status: "unknown",
|
|
431
|
-
}),
|
|
432
|
-
indexStatus: () => get("/api/index/status", {}),
|
|
433
|
-
rebuildIndex: () => post("/api/index/rebuild", { full: false, include_nodes: true, include_chunks: true }, {}),
|
|
434
|
-
graph: () => get("/knowledge-graph/graph", { nodes: [], edges: [] }),
|
|
435
|
-
graphStats: () => get("/knowledge-graph/stats", { nodes: {}, edges: {}, total_nodes: 0, total_edges: 0 }),
|
|
436
|
-
graphPortability: () => get("/api/knowledge-graph/portability", {}),
|
|
437
|
-
brainStorage: () => get("/api/brain/storage", {}),
|
|
438
|
-
backupHealth: () => get("/api/knowledge-graph/backup-health", {}),
|
|
439
|
-
dockerPostgres: (body: { consent: boolean; dry_run?: boolean; port?: number }) => post("/api/brain/storage/postgres/docker", body, {}),
|
|
440
|
-
migratePostgres: (body: { dsn: string; schema_name?: string; dry_run?: boolean }) => post("/api/brain/storage/migrate-postgres", body, {}),
|
|
441
|
-
graphProvenance: (limit = 50) => get("/api/knowledge-graph/provenance", { items: [] }, { limit }),
|
|
442
|
-
graphCoverage: () => get("/knowledge-graph/provenance/coverage", {}),
|
|
443
|
-
graphExport: () => post("/api/knowledge-graph/export", {}, {}),
|
|
444
|
-
graphBackup: () => post("/api/knowledge-graph/backup", {}, {}),
|
|
445
|
-
graphImport: (artifact: unknown, dry_run = true) => post("/api/knowledge-graph/import", { artifact, mode: "merge", dry_run }, {}),
|
|
446
|
-
brainArchive: (body: { path?: string | null; passphrase: string }) => post("/api/knowledge-graph/archive", body, {}),
|
|
447
|
-
brainArchiveInspect: (body: { path: string; passphrase?: string | null }) => post("/api/knowledge-graph/archive/inspect", body, {}),
|
|
448
|
-
brainArchiveVerify: (body: { path: string; passphrase: string }) => post("/api/knowledge-graph/archive/verify", body, {}),
|
|
449
|
-
brainArchiveRestore: (body: { path: string; passphrase: string; dry_run?: boolean; confirm?: boolean }) => post("/api/knowledge-graph/archive/restore", body, {}),
|
|
450
|
-
brainArchiveImport: (body: { path: string; passphrase: string; dry_run?: boolean; confirm?: boolean }) => post("/api/knowledge-graph/archive/import", body, {}),
|
|
451
|
-
hybridSearch: async (query: string, weights?: unknown) => {
|
|
452
|
-
const res = await post<Record<string, unknown>>("/api/search/hybrid", { query, ...(weights ? { weights } : {}) }, { matches: [] });
|
|
453
|
-
const data = res.data as Record<string, unknown>;
|
|
454
|
-
if (res.ok && !Array.isArray(data.matches) && Array.isArray(data.results)) {
|
|
455
|
-
return { ...res, data: { ...data, matches: data.results } };
|
|
456
|
-
}
|
|
457
|
-
return res;
|
|
458
|
-
},
|
|
459
|
-
browserReadUrl: (url: string) => post("/api/browser/read-url", { url }, {}),
|
|
460
|
-
ingestNote: (content: string, title = "Brain note") => post(
|
|
461
|
-
"/knowledge-graph/ingest",
|
|
462
|
-
{ type: "note", content, title, source: "brain_home" },
|
|
463
|
-
{},
|
|
464
|
-
),
|
|
465
|
-
memoryManager: () => get("/api/memory/manager", { sources: [], tiers: [], usage: {} }),
|
|
466
|
-
memoryBrainQuality: () => get("/api/memory/brain-quality", {}),
|
|
467
|
-
memoryBrainProof: (query = "", limit = 3) => get("/api/memory/brain-proof", { proofs: {}, recall: { items: [] }, model_continuity: {}, claims: {} }, { q: query, limit }),
|
|
468
|
-
memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
|
|
469
|
-
memoryCompact: () => post("/api/memory/compact", {}, {}),
|
|
470
|
-
memoryRebuild: () => post("/api/memory/rebuild", { target: "vector" }, {}),
|
|
471
|
-
chatHistory: () => get("/history/conversations", []),
|
|
472
|
-
conversation: (id: string) => get(`/history/conversations/${encodeURIComponent(id)}`, { messages: [] }),
|
|
473
|
-
deleteConversation: (id: string) => del(`/history/conversations/${encodeURIComponent(id)}`, {}),
|
|
474
|
-
streamChat,
|
|
475
|
-
uploadDocument,
|
|
476
|
-
documents: (limit = 200) => get("/knowledge-graph/documents", { documents: [] }, { limit }),
|
|
477
|
-
localSources: () => get("/knowledge-graph/local/sources", { sources: [], watch: { available: false, active: {} } }),
|
|
478
|
-
localAgent: () => get("/api/local-agent/status", { agent: { online: false }, sources: [] }),
|
|
479
|
-
connectFolder: (path: string) => post("/knowledge-graph/local/index", { path, approved: true, watch_enabled: true, consent: { approved: true, source: "desktop-spa" } }, {}),
|
|
480
|
-
localWatchStop: (source_id: string) => post("/knowledge-graph/local/watch/stop", { source_id }, {}),
|
|
481
|
-
models: () => get("/models", { catalog: [], loaded: [], recommended: [] }),
|
|
482
|
-
setupScan: () => get("/setup/scan", { environment: {}, recommendations: {}, zero_config: {} }),
|
|
483
|
-
modelRecommendations: (engine = "local_mlx") => get("/models/recommendations", { profile: {}, recommendations: { models: [], families: [], counts: {} } }, { engine }),
|
|
484
|
-
installEngine: (engine: string) => post("/engines/install", { engine }, {}),
|
|
485
|
-
prepareModel: (model: string, engine?: string, allow_download = false) => post("/engines/prepare-model", { model, engine: engine || null, allow_download }, {}),
|
|
486
|
-
streamModelPrepare,
|
|
487
|
-
loadModel: (model_id: string, engine?: string, allow_download = false) => post("/models/load", { model_id, engine: engine || null, allow_download }, {}),
|
|
488
|
-
unloadModel: (model_id: string) => del(`/models/unload/${encodeURIComponent(model_id)}`, {}),
|
|
489
|
-
embeddingsStatus: () => get("/api/embeddings/status", {}),
|
|
490
|
-
agentRuntime: () => get("/agents/api/runtime/status", { runtime: {}, agents: [], runs: [] }),
|
|
491
|
-
agentRunPreview: (goal: string, roles: string[] = []) => post("/agents/api/run/preview", { goal, roles }, { ready: false, blocking_reasons: [] }),
|
|
492
|
-
runAgent: (goal: string, roles: string[]) => post("/agents/api/run", { goal, roles }, {}),
|
|
493
|
-
toolRegistryDiagnostics: () => get("/tools/registry/diagnostics", { diagnostics: { ready: false } }),
|
|
494
|
-
toolRegistry: () => get("/tools/registry", { status: "unavailable", diagnostics: {}, tools: [] }),
|
|
495
|
-
agentRun: (id: string) => get(`/agents/api/runs/${encodeURIComponent(id)}`, {}),
|
|
496
|
-
stopAgentRun: (id: string) => post(`/agents/api/runs/${encodeURIComponent(id)}/stop`, {}, {}),
|
|
497
|
-
agentRegistry: () => get("/agents/api/registry", { agents: [] }),
|
|
498
|
-
agentCapabilities: () => get("/agents/api/registry/capabilities", { capabilities: {} }),
|
|
499
|
-
registerAgent: (body: unknown) => post("/agents/api/registry", body, {}),
|
|
500
|
-
updateAgent: (id: string, body: unknown) => patch(`/agents/api/registry/${encodeURIComponent(id)}`, body, {}),
|
|
501
|
-
removeAgent: (id: string) => del(`/agents/api/registry/${encodeURIComponent(id)}`, {}),
|
|
502
|
-
workflowDefinitions: () => get("/workflows/api/definitions", { workflows: [] }),
|
|
503
|
-
workflowRuns: () => get("/workflows/api/runs", { runs: [] }),
|
|
504
|
-
workflowTriggers: () => get("/workflows/api/triggers", { armed: [] }),
|
|
505
|
-
automationRecipes: () => get("/workflows/api/automation/recipes", { recipes: [], principles: {} }),
|
|
506
|
-
installAutomationRecipe: (recipeId: string, enabled = false) => post(`/workflows/api/automation/recipes/${encodeURIComponent(recipeId)}`, { enabled }, {}),
|
|
507
|
-
createWorkflow: (body: { name: string; nodes: Array<Record<string, unknown>>; metadata?: Record<string, unknown> }) => post("/workflows/api/definitions", body, {}),
|
|
508
|
-
importWorkflow: (data: Record<string, unknown>) => post("/workflows/api/import", { data }, {}),
|
|
509
|
-
exportWorkflow: (id: string) => get(`/workflows/api/export/${encodeURIComponent(id)}`, {}),
|
|
510
|
-
runWorkflow: (id: string) => post(`/workflows/api/definitions/${encodeURIComponent(id)}/run`, {}, {}),
|
|
511
|
-
updateWorkflow: (id: string, body: unknown) => patch(`/workflows/api/definitions/${encodeURIComponent(id)}`, body, {}),
|
|
512
|
-
stopWorkflowRun: (id: string) => post(`/workflows/api/runs/${encodeURIComponent(id)}/stop`, {}, {}),
|
|
513
|
-
resumeWorkflowRun: (id: string, approved: boolean) => post(`/workflows/api/runs/${encodeURIComponent(id)}/resume`, { approved }, {}),
|
|
514
|
-
hooks: () => get("/api/hooks", { hooks: [] }),
|
|
515
|
-
hookRuns: () => get("/api/hooks/runs", { runs: [] }, { limit: 50 }),
|
|
516
|
-
hookRun: (body: unknown) => post("/api/hooks/run", body, {}),
|
|
517
|
-
automationReviews: reviewList,
|
|
518
|
-
approveReviewItem: (id: string) => reviewAction(id, "approve"),
|
|
519
|
-
dismissReviewItem: (id: string) => reviewAction(id, "dismiss"),
|
|
520
|
-
snoozeReviewItem: snoozeReview,
|
|
521
|
-
unsnoozeReviewItem: (id: string) => reviewAction(id, "unsnooze"),
|
|
522
|
-
runNowReviewItem: (id: string) => reviewAction(id, "run_now"),
|
|
523
|
-
permissionsPending: () => get("/permissions/pending", { pending: {}, count: 0 }),
|
|
524
|
-
approvePermission: (token: string) => post(`/permissions/approve/${encodeURIComponent(token)}`, {}, {}),
|
|
525
|
-
denyPermission: (token: string) => post(`/permissions/deny/${encodeURIComponent(token)}`, {}, {}),
|
|
526
|
-
skills: () => get("/workspace/skills", { skills: [] }),
|
|
527
|
-
skillToggle: (skill: string, enabled: boolean) => post(enabled ? "/workspace/skills/disable" : "/workspace/skills/enable", { skill }, {}),
|
|
528
|
-
skillsMarketplace: () => get("/skills/marketplace", { skills: [] }),
|
|
529
|
-
skillInstall: (skill: string, plugin?: string) => post("/workspace/skills/install", { skill, plugin: plugin || "" }, {}),
|
|
530
|
-
mcpTools: () => get("/mcp/tools", { tools: [], installed_mcps: [] }),
|
|
531
|
-
mcpRecommend: (query: string) => post("/mcp/recommend", { query, limit: 6 }, {}),
|
|
532
|
-
templates: () => get("/marketplace/templates", { templates: [], kinds: [] }),
|
|
533
|
-
installTemplate: (data: unknown) => post("/marketplace/templates/install", { data }, {}),
|
|
534
|
-
pluginsRegistry: () => get("/plugins/registry", { plugins: [] }),
|
|
535
|
-
pluginsDirectory: () => get("/plugins/directory", { plugins: [] }),
|
|
536
|
-
profile: () => get("/account/profile", {}),
|
|
537
|
-
login: (email: string, password: string) => post("/login", { email, password }, {}),
|
|
538
|
-
register: (body: unknown) => post("/register", body, {}),
|
|
539
|
-
logout: () => post("/logout", {}, {}),
|
|
540
|
-
updateProfile: (body: unknown) => patch("/account/profile", body, {}),
|
|
541
|
-
changePassword: (current_password: string, new_password: string) => post("/account/change-password", { current_password, new_password }, {}),
|
|
542
|
-
ssoConfig: () => get("/auth/sso/config", { enabled: false, providers: [] }),
|
|
543
|
-
workspaceRegistry: () => get("/workspace/registry", { workspaces: [] }),
|
|
544
|
-
createOrg: (name: string) => post("/workspace/orgs", { name }, {}),
|
|
545
|
-
activateWorkspace: (workspace_id: string) => post("/workspace/activate", { workspace_id }, {}),
|
|
546
|
-
archiveWorkspace: (workspace_id: string) => post(`/workspace/orgs/${encodeURIComponent(workspace_id)}/archive`, {}, {}),
|
|
547
|
-
addWorkspaceMember: (workspace_id: string, user_id: string, role: string) => post(`/workspace/orgs/${encodeURIComponent(workspace_id)}/members`, { user_id, role }, {}),
|
|
548
|
-
removeWorkspaceMember: (workspace_id: string, user_id: string) => del(`/workspace/orgs/${encodeURIComponent(workspace_id)}/members/${encodeURIComponent(user_id)}`, {}),
|
|
549
|
-
invitations: () => get("/invitations", { invitations: [] }),
|
|
550
|
-
createInvitation: (body: unknown) => post("/invitations", body, {}),
|
|
551
|
-
acceptInvitation: (token: string) => post(`/invitations/${encodeURIComponent(token)}/accept`, {}, {}),
|
|
552
|
-
snapshots: () => get("/workspace/snapshots", { snapshots: [] }),
|
|
553
|
-
createSnapshot: (name: string) => post("/workspace/snapshots", { name }, {}),
|
|
554
|
-
compareSnapshots: (before_id: string, after_id: string) => post("/workspace/snapshots/compare", { before_id, after_id }, {}),
|
|
555
|
-
restoreSnapshot: (id: string) => post(`/workspace/snapshots/${encodeURIComponent(id)}/restore`, {}, {}),
|
|
556
|
-
exportSnapshot: (id: string) => post(`/workspace/snapshots/${encodeURIComponent(id)}/export`, {}, {}),
|
|
557
|
-
timeMachine: () => get("/workspace/time-machine", { events: [] }, { limit: 100 }),
|
|
558
|
-
realtimeFeed: () => get("/realtime/feed", { events: [] }, { limit: 80 }),
|
|
559
|
-
presence: () => get("/realtime/presence", { presence: [] }),
|
|
560
|
-
networkIdentity: () => get("/network/identity", {}),
|
|
561
|
-
networkPeers: () => get("/network/peers", { peers: [] }),
|
|
562
|
-
pairPeer: (body: unknown) => post("/network/peers", body, {}),
|
|
563
|
-
unpairPeer: (id: string) => del(`/network/peers/${encodeURIComponent(id)}`, {}),
|
|
564
|
-
pushPeer: (id: string, workspace_id?: string | null) => post(`/network/push/${encodeURIComponent(id)}`, { workspace_id }, {}),
|
|
565
|
-
sysinfo: () => get("/local/sysinfo", {}),
|
|
566
|
-
computerMemory: () => get("/workspace/computer-memory", {}),
|
|
567
|
-
setComputerMemory: (enabled: boolean) => post("/workspace/computer-memory", { enabled, consent: { approved: enabled } }, {}),
|
|
568
|
-
adminSummary: () => get("/admin/summary", {}),
|
|
569
|
-
adminStats: () => get("/admin/stats", {}),
|
|
570
|
-
adminUsers: () => get("/admin/users", []),
|
|
571
|
-
adminAudit: (filters?: AdminAuditFilters) => get("/admin/audit", { recent_events: [], filters: {} }, filters),
|
|
572
|
-
adminRoles: () => get("/admin/roles", { roles: [] }),
|
|
573
|
-
adminPolicies: () => get("/admin/policies", { policies: [] }),
|
|
574
|
-
adminLogRetention: () => get("/admin/log-retention", {}),
|
|
575
|
-
adminProductHardening: () => get("/admin/product-hardening", {}),
|
|
576
|
-
adminSecurity: () => get("/admin/security/overview", {}),
|
|
577
|
-
adminSecurityEvents: (limit = 50) => get("/admin/security/events", { events: [] }, { limit }),
|
|
578
|
-
vpcStatus: () => get("/vpc/status", {}),
|
|
579
|
-
toolPermissions: () => get("/tools/permissions", { permissions: [] }),
|
|
580
|
-
};
|