ltcai 8.0.0 → 8.2.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 (183) hide show
  1. package/README.md +33 -72
  2. package/docs/CHANGELOG.md +52 -874
  3. package/docs/DEVELOPMENT.md +26 -3
  4. package/docs/LEGACY_COMPATIBILITY.md +3 -1
  5. package/docs/TRUST_MODEL.md +26 -26
  6. package/docs/WHY_LATTICE.md +28 -25
  7. package/docs/kg-schema.md +6 -4
  8. package/lattice_brain/__init__.py +1 -1
  9. package/lattice_brain/embeddings.py +9 -1
  10. package/lattice_brain/graph/schema.py +6 -0
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/computer_use.py +2 -0
  14. package/latticeai/api/memory.py +13 -0
  15. package/latticeai/app_factory.py +43 -162
  16. package/latticeai/brain/__init__.py +1 -1
  17. package/latticeai/core/config.py +10 -0
  18. package/latticeai/core/local_embeddings.py +8 -0
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/mcp_registry.py +140 -1
  21. package/latticeai/core/tool_registry.py +10 -0
  22. package/latticeai/core/workspace_graph_trace.py +132 -0
  23. package/latticeai/core/workspace_memory.py +75 -0
  24. package/latticeai/core/workspace_os.py +135 -1283
  25. package/latticeai/core/workspace_os_utils.py +132 -0
  26. package/latticeai/core/workspace_permissions.py +99 -0
  27. package/latticeai/core/workspace_plugins.py +97 -0
  28. package/latticeai/core/workspace_runs.py +612 -0
  29. package/latticeai/core/workspace_skills.py +114 -0
  30. package/latticeai/core/workspace_snapshots.py +195 -0
  31. package/latticeai/core/workspace_timeline.py +107 -0
  32. package/latticeai/runtime/audit_runtime.py +64 -0
  33. package/latticeai/runtime/config_runtime.py +4 -0
  34. package/latticeai/runtime/sso_runtime.py +52 -0
  35. package/latticeai/services/architecture_readiness.py +6 -5
  36. package/latticeai/services/memory_service.py +215 -7
  37. package/latticeai/services/model_capability_registry.py +68 -0
  38. package/latticeai/services/model_engines.py +603 -0
  39. package/latticeai/services/model_loading.py +482 -0
  40. package/latticeai/services/model_recommendation.py +5 -0
  41. package/latticeai/services/model_runtime.py +146 -837
  42. package/latticeai/services/product_readiness.py +19 -18
  43. package/package.json +2 -2
  44. package/scripts/build_frontend_assets.mjs +11 -2
  45. package/src-tauri/Cargo.lock +1 -1
  46. package/src-tauri/Cargo.toml +1 -1
  47. package/src-tauri/tauri.conf.json +1 -1
  48. package/static/app/asset-manifest.json +11 -102
  49. package/static/app/assets/Act-D9jIknFd.js +1 -0
  50. package/static/app/assets/Brain-CFOtWbPN.js +321 -0
  51. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
  52. package/static/app/assets/Library-C5Q2yWee.js +1 -0
  53. package/static/app/assets/System-BLbjdr1_.js +1 -0
  54. package/static/app/assets/core-CwxXejkd.js +1 -2
  55. package/static/app/assets/index-BqammyNu.js +16 -0
  56. package/static/app/assets/index-ty1iGgZu.css +2 -0
  57. package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
  58. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
  59. package/static/app/index.html +3 -10
  60. package/static/app/theme-boot.js +8 -0
  61. package/static/sw.js +1 -1
  62. package/tools/__init__.py +2 -1
  63. package/tools/computer.py +21 -0
  64. package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
  65. package/docs/HANDOVER_v3.6.0.md +0 -46
  66. package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
  67. package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
  68. package/docs/V2_ARCHITECTURE.md +0 -561
  69. package/docs/V3_2_AUDIT.md +0 -82
  70. package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
  71. package/docs/V3_FRONTEND.md +0 -140
  72. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
  73. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
  74. package/docs/V4_1_VALIDATION_REPORT.md +0 -47
  75. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
  76. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
  77. package/docs/V4_2_VALIDATION_REPORT.md +0 -89
  78. package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
  79. package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
  80. package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
  81. package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
  82. package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
  83. package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
  84. package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
  85. package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
  86. package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
  87. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
  88. package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
  89. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
  90. package/docs/V4_3_VALIDATION_REPORT.md +0 -58
  91. package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
  92. package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
  93. package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
  94. package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
  95. package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
  96. package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
  97. package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
  98. package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
  99. package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
  100. package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
  101. package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
  102. package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
  103. package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
  104. package/docs/V4_5_1_UX_REPORT.md +0 -45
  105. package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
  106. package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
  107. package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
  108. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
  109. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
  110. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
  111. package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
  112. package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
  113. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
  114. package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
  115. package/frontend/index.html +0 -24
  116. package/frontend/openapi.json +0 -15425
  117. package/frontend/src/App.tsx +0 -243
  118. package/frontend/src/api/client.ts +0 -580
  119. package/frontend/src/api/openapi.ts +0 -17892
  120. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  121. package/frontend/src/components/FeedbackState.tsx +0 -45
  122. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  123. package/frontend/src/components/LivingBrain.tsx +0 -220
  124. package/frontend/src/components/ProductFlow.tsx +0 -171
  125. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  126. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  127. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  128. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  129. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  130. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  131. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  132. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  133. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  134. package/frontend/src/components/primitives.tsx +0 -392
  135. package/frontend/src/components/ui/badge.tsx +0 -27
  136. package/frontend/src/components/ui/button.tsx +0 -37
  137. package/frontend/src/components/ui/card.tsx +0 -22
  138. package/frontend/src/components/ui/input.tsx +0 -16
  139. package/frontend/src/components/ui/textarea.tsx +0 -16
  140. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  141. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  142. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  143. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  144. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  145. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  146. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  147. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  148. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  149. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  150. package/frontend/src/features/brain/brainData.ts +0 -246
  151. package/frontend/src/features/brain/graphLayout.ts +0 -37
  152. package/frontend/src/features/brain/types.ts +0 -150
  153. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  154. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  155. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  156. package/frontend/src/i18n.ts +0 -1303
  157. package/frontend/src/lib/utils.ts +0 -33
  158. package/frontend/src/main.tsx +0 -23
  159. package/frontend/src/pages/Act.tsx +0 -458
  160. package/frontend/src/pages/Ask.tsx +0 -14
  161. package/frontend/src/pages/Brain.tsx +0 -914
  162. package/frontend/src/pages/Capture.tsx +0 -258
  163. package/frontend/src/pages/Library.tsx +0 -486
  164. package/frontend/src/pages/System.tsx +0 -621
  165. package/frontend/src/routes.ts +0 -92
  166. package/frontend/src/store/appStore.ts +0 -94
  167. package/frontend/src/styles.css +0 -6579
  168. package/static/app/assets/Act-DOvf59ru.js +0 -2
  169. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  170. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  171. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  172. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  173. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  174. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  175. package/static/app/assets/System-D6t9jo9V.js +0 -2
  176. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  177. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  178. package/static/app/assets/index-C7g26IF6.css +0 -2
  179. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  180. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  181. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  182. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  183. 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
- };