ltcai 5.6.0 → 6.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.
Files changed (55) hide show
  1. package/README.md +45 -25
  2. package/docs/CHANGELOG.md +74 -0
  3. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +1 -1
  4. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +1 -1
  5. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +1 -1
  6. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +1 -1
  7. package/frontend/openapi.json +39 -0
  8. package/frontend/src/App.tsx +5 -0
  9. package/frontend/src/api/client.ts +104 -23
  10. package/frontend/src/api/openapi.ts +48 -0
  11. package/frontend/src/components/FirstRunGuide.tsx +3 -3
  12. package/frontend/src/components/ProductFlow.tsx +7 -0
  13. package/frontend/src/features/review/ReviewCard.tsx +96 -0
  14. package/frontend/src/features/review/ReviewInbox.tsx +112 -0
  15. package/frontend/src/features/review/reviewHelpers.ts +69 -0
  16. package/frontend/src/i18n.ts +18 -8
  17. package/frontend/src/pages/Act.tsx +5 -177
  18. package/frontend/src/routes.ts +1 -0
  19. package/frontend/src/styles.css +20 -0
  20. package/lattice_brain/__init__.py +1 -1
  21. package/lattice_brain/runtime/multi_agent.py +1 -1
  22. package/latticeai/__init__.py +1 -1
  23. package/latticeai/api/chat.py +52 -33
  24. package/latticeai/api/review_queue.py +7 -3
  25. package/latticeai/app_factory.py +253 -475
  26. package/latticeai/cli/__init__.py +1 -0
  27. package/latticeai/cli/runtime.py +37 -0
  28. package/latticeai/core/marketplace.py +1 -1
  29. package/latticeai/core/workspace_os.py +1 -1
  30. package/latticeai/runtime/app_context_runtime.py +13 -0
  31. package/latticeai/runtime/automation_runtime.py +64 -0
  32. package/latticeai/runtime/bootstrap.py +48 -0
  33. package/latticeai/runtime/context_runtime.py +43 -0
  34. package/latticeai/runtime/hooks_runtime.py +77 -0
  35. package/latticeai/runtime/lifespan_runtime.py +138 -0
  36. package/latticeai/runtime/persistence_runtime.py +87 -0
  37. package/latticeai/runtime/platform_services_runtime.py +39 -0
  38. package/latticeai/runtime/router_registration.py +570 -0
  39. package/latticeai/runtime/web_runtime.py +65 -0
  40. package/latticeai/services/app_context.py +1 -0
  41. package/latticeai/services/review_queue.py +20 -4
  42. package/latticeai/services/tool_dispatch.py +82 -25
  43. package/ltcai_cli.py +5 -31
  44. package/package.json +1 -1
  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 +5 -5
  49. package/static/app/assets/{index-xRn29gI8.css → index-B744yblP.css} +1 -1
  50. package/static/app/assets/index-DYaUKNfl.js +16 -0
  51. package/static/app/assets/index-DYaUKNfl.js.map +1 -0
  52. package/static/app/index.html +2 -2
  53. package/telegram_bot.py +3 -9
  54. package/static/app/assets/index-xMFu94cX.js +0 -16
  55. package/static/app/assets/index-xMFu94cX.js.map +0 -1
package/README.md CHANGED
@@ -69,35 +69,42 @@ You need Lattice AI when:
69
69
  Choose the owner of the Brain. The profile is not a SaaS account by default; it
70
70
  is the local identity for the knowledge you keep.
71
71
 
72
- ![Login](output/release/v5.3.0/screenshots/01-login.png)
72
+ ![Login](output/release/v6.1.0/screenshots/01-login.png)
73
73
 
74
74
  ### 2. Environment Analysis
75
75
 
76
76
  See what kind of local AI experience this computer can support before choosing a
77
77
  model.
78
78
 
79
- ![Environment Analysis](output/release/v5.3.0/screenshots/02-environment-analysis.png)
79
+ ![Environment Analysis](output/release/v6.1.0/screenshots/02-environment-analysis.png)
80
80
 
81
81
  ### 3. Recommended Models
82
82
 
83
83
  Start with a short list: safest recommendation, faster model, stronger model.
84
84
  Advanced details stay available without overwhelming first-time users.
85
85
 
86
- ![Recommended Models](output/release/v5.3.0/screenshots/03-recommended-models.png)
86
+ ![Recommended Models](output/release/v6.1.0/screenshots/03-recommended-models.png)
87
87
 
88
88
  ### 4. Install And Load
89
89
 
90
90
  Download and load only after consent. Lattice explains model size, local
91
91
  execution, and network use before work starts.
92
92
 
93
- ![Install and Load](output/release/v5.3.0/screenshots/04-install-load-progress.png)
93
+ ![Install and Load](output/release/v6.1.0/screenshots/04-install-load-progress.png)
94
94
 
95
95
  ### 5. Brain Chat
96
96
 
97
97
  Talk normally. Useful decisions and context become memory, then appear later as
98
98
  topics, relationships, and graph structure.
99
99
 
100
- ![Brain Chat Home](output/release/v5.3.0/screenshots/05-brain-chat-home.png)
100
+ ![Brain Chat Home](output/release/v6.1.0/screenshots/05-brain-chat-home.png)
101
+
102
+ ### 6. Review Center
103
+
104
+ Automation results are staged for review before they become durable decisions.
105
+ Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
106
+
107
+ ![Review Center](output/release/v6.1.0/screenshots/13-review-center.png)
101
108
 
102
109
  ## Brain Depths
103
110
 
@@ -113,10 +120,10 @@ The user travels inward from everyday memory to deeper structure:
113
120
 
114
121
  Walkthrough:
115
122
 
116
- ![v5.3.0 Living Brain walkthrough](output/release/v5.3.0/gifs/v5.3.0-living-brain-walkthrough.gif)
123
+ ![v6.1.0 Living Brain walkthrough](output/release/v6.1.0/gifs/v6.1.0-living-brain-walkthrough.gif)
117
124
 
118
125
  Screenshot index and capture notes:
119
- [output/release/v5.3.0/SCREENSHOT_INDEX.md](output/release/v5.3.0/SCREENSHOT_INDEX.md)
126
+ [output/release/v6.1.0/SCREENSHOT_INDEX.md](output/release/v6.1.0/SCREENSHOT_INDEX.md)
120
127
 
121
128
  ## Install
122
129
 
@@ -193,24 +200,36 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
193
200
 
194
201
  ## Current Release Preparation
195
202
 
196
- The current development target is **5.6.0 Brain Automation Review Center**:
197
-
198
- - Automation output now lands in a workspace-scoped Review inbox before users
199
- approve, dismiss, snooze, or rerun suggestions.
200
- - `/automation/reviews` exposes source-aware review items with provenance,
201
- `effective_status`, and guarded actions.
202
- - TriggerService and RunExecutor can enqueue review items only when workflows
203
- explicitly opt in with `review_queue: true`.
204
- - Act now includes a Review tab under Runs for pending automation suggestions.
205
- - All package/runtime/static/OpenAPI versions are synchronized to 5.6.0.
206
-
207
- Expected artifacts for 5.6.0 release must use exact filenames:
208
-
209
- - `dist/ltcai-5.6.0-py3-none-any.whl`
210
- - `dist/ltcai-5.6.0.tar.gz`
211
- - `ltcai-5.6.0.tgz`
212
- - `dist/ltcai-5.6.0.vsix`
213
- - `src-tauri/target/release/bundle/dmg/Lattice AI_5.6.0_aarch64.dmg`
203
+ The current development target is **6.1.0 Product Hardening / Digital Brain Completion**:
204
+
205
+ - First-run now lets users create or open the local Brain even when they defer
206
+ model setup, keeping the Brain durable while models stay replaceable.
207
+ - Brain Home explains the first memory loop: save useful context, see it return
208
+ in the Brain state, then protect it with backup/export.
209
+ - Review Center keeps Pending, Snoozed, and All filters while clarifying that
210
+ Run now is a preview/regenerate action, not approval.
211
+ - `lattice_brain` has an AST import guard so Brain Core does not import
212
+ `latticeai` or `ltcai`.
213
+ - Model download consent has a unit guard: token presence alone does not start
214
+ an external download path.
215
+ - `ltcai_cli.py` keeps the root entrypoint while pure CLI runtime helpers live
216
+ under `latticeai.cli.runtime`.
217
+ - Tool dispatch authorization now has an injectable service boundary around
218
+ the shared ToolRegistry, reducing app-factory reliance on module globals.
219
+ - Chat agent runtime construction moved into app-factory assembly and is passed
220
+ through `AppContext`, so the chat router no longer owns production runtime
221
+ construction.
222
+ - v6.1 documentation tracks backend hardening, frontend UX hardening, and the
223
+ baseline scan for version metadata, root legacy modules, Brain Core boundary,
224
+ and local-first trust gates.
225
+
226
+ Expected artifacts for 6.1.0 release must use exact filenames:
227
+
228
+ - `dist/ltcai-6.1.0-py3-none-any.whl`
229
+ - `dist/ltcai-6.1.0.tar.gz`
230
+ - `ltcai-6.1.0.tgz`
231
+ - `dist/ltcai-6.1.0.vsix`
232
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_6.1.0_aarch64.dmg`
214
233
 
215
234
  Do not upload `dist/*`. Package registry publishing remains owner-run.
216
235
 
@@ -229,6 +248,7 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
229
248
 
230
249
  | Version | Theme |
231
250
  | --- | --- |
251
+ | 6.0.0 | Product Reset / Review Center Completion: Snoozed filter, Unsnooze, OpenAPI-derived Review typing, Review feature extraction, v6 docs and scorecard |
232
252
  | 5.6.0 | Brain Automation Review Center: workspace-scoped automation review inbox, source-aware provenance, guarded approve/dismiss/snooze/run_now actions, and Act Review tab |
233
253
  | 5.5.0 | Release Coordination: synchronized package/runtime/static versions and release docs for the 5.5.0 line while preserving v5.4.0 Brain Automation Scheduler behavior |
234
254
  | 5.4.0 | Brain Automation Scheduler: consent-first recipe drafts (Daily/Weekly/Follow-up), TriggerService with dedup/LATTICE_TZ/degraded, runtime graph cleanup, E2E scenarios |
package/docs/CHANGELOG.md CHANGED
@@ -3,6 +3,80 @@
3
3
  The top entry is the current release-preparation target. Older entries are
4
4
  historical and may describe behavior as it existed at that release.
5
5
 
6
+ ## [6.1.0] - 2026-06-16
7
+
8
+ > Product Hardening / Digital Brain Completion. Tightens the local-first Brain
9
+ > flow, Brain Core boundary, backend trust gates, and release documentation
10
+ > without adding broad new product surfaces.
11
+
12
+ ### Added
13
+ - First-run path to open the Brain without installing or loading a model first.
14
+ - Brain Home empty-state loop that shows first memory save, Brain state return,
15
+ and backup ownership.
16
+ - AST import guard proving `lattice_brain` does not import `latticeai` or
17
+ `ltcai`.
18
+ - Unit coverage for model download consent blocking in `model_runtime`.
19
+ - `latticeai.cli.runtime` for pure CLI runtime helpers while preserving the root
20
+ `ltcai_cli.py` entrypoint.
21
+ - v6.1 baseline, frontend UX, backend hardening, and instruction documents under
22
+ `docs/v6.1/`.
23
+
24
+ ### Changed
25
+ - Review Center cards clarify that Run now previews/executes without approval.
26
+ - Static app assets are refreshed for the v6.1 frontend hardening changes.
27
+ - Tool dispatch authorization now uses an injectable service boundary around
28
+ the shared ToolRegistry while preserving the legacy module-level API.
29
+ - Chat agent runtime construction moved out of `create_chat_router` for
30
+ production app assembly and is now injected through `AppContext`.
31
+ - README release evidence screenshots and walkthrough GIF are refreshed under
32
+ `output/release/v6.1.0/`, including the Review Center surface.
33
+ - README and release notes now describe v6.1.0 as the current hardening target
34
+ with exact expected artifact filenames.
35
+
36
+ ### Preserved
37
+ - Package publish, deployment, tag creation, and main merge remain out of scope
38
+ unless explicitly requested.
39
+ - Package/runtime version metadata is synchronized to 6.1.0; package publish,
40
+ deployment, tag creation, and main merge remain owner-run only.
41
+
42
+ ## [6.0.0] - 2026-06-15
43
+
44
+ > Product Reset / Review Center Completion. Raises the Review Center from a
45
+ > pending-only inbox into a reversible automation review surface while
46
+ > documenting the v6 quality uplift honestly.
47
+
48
+ ### Added
49
+ - Review Center status filters for Pending, Snoozed, and All.
50
+ - Explicit `POST /automation/reviews/{item_id}/unsnooze` API and backend policy.
51
+ - Frontend Unsnooze action and clear `snoozed_until` presentation.
52
+ - `docs/v6/PLAN.md`, `ARCHITECTURE_REVIEW.md`, `UX_REVIEW.md`, and
53
+ `QUALITY_SCORECARD.md`.
54
+
55
+ ### Changed
56
+ - Review Center frontend moved from `Act.tsx` into
57
+ `frontend/src/features/review/` components and helpers.
58
+ - Review item frontend types now alias generated OpenAPI component schemas.
59
+ - Review Center API calls now use generated OpenAPI operation paths for list,
60
+ approve, dismiss, snooze, unsnooze, and run_now actions.
61
+ - `app_factory.py` runtime assembly moved behind session, hooks, web,
62
+ persistence, lifespan, automation, context/search, platform service, app
63
+ context, and router-registration seams while preserving the frozen 364-entry
64
+ route/mount snapshot.
65
+ - First-run/onboarding copy now states local-first trust boundaries more
66
+ directly: local knowledge by default, explicit downloads, and explicit
67
+ external transfer.
68
+ - README release evidence screenshots and walkthrough GIF are refreshed under
69
+ `output/release/v6.0.0/`, including the Review Center surface.
70
+ - OpenAPI artifacts and synchronized package/runtime/static metadata now target
71
+ `6.0.0`.
72
+
73
+ ### Preserved
74
+ - `run_now` remains preview/regenerate and does not approve.
75
+ - Snooze expiry remains read-time only; explicit unsnooze is the only immediate
76
+ return-to-pending mutation.
77
+ - Package publishing, GitHub Release creation, artifact upload, and merge to
78
+ `main` remain out of scope for this branch.
79
+
6
80
  ## [5.6.0] - 2026-06-15
7
81
 
8
82
  > Brain Automation Review Center. Adds a workspace-scoped review inbox for
@@ -61,7 +61,7 @@ contracts, and local-first/offline operation.
61
61
  - Legacy frontend build pipeline.
62
62
  - Legacy v3 frontend lint script.
63
63
  - Static v3 asset tree in release packages.
64
- - Direct current-release references to v4.0.1 in latest docs.
64
+ - Direct then-current release references to v4.0.1 in then-latest docs.
65
65
 
66
66
  ## Data Preservation
67
67
 
@@ -8,7 +8,7 @@ architecture or add unrelated product features.
8
8
 
9
9
  ## Completed Refresh Areas
10
10
 
11
- - Synchronized current-release version metadata to `4.6.1`.
11
+ - Synchronized then-current release version metadata to `4.6.1`.
12
12
  - Reframed README around the current Living Brain flow:
13
13
  Login -> Environment Analysis -> Recommended Models -> Install & Load ->
14
14
  Brain Chat.
@@ -19,7 +19,7 @@ maintenance. This release does not redesign Brain Core or the storage layer.
19
19
  - Added frontend API helpers for `/admin/stats` and `/admin/security/events`.
20
20
  - Updated visual validation to confirm the admin console is separate from the
21
21
  user Brain surface.
22
- - Synchronized current-release version metadata to `4.7.0`.
22
+ - Synchronized then-current release version metadata to `4.7.0`.
23
23
  - Updated README, RELEASE.md, release notes, changelog, architecture, security,
24
24
  feature status, VS Code extension docs, and release evidence references.
25
25
 
@@ -26,7 +26,7 @@ maintenance. This release does not redesign Brain Core or the storage layer.
26
26
  admin observability state remain separate in the frontend runtime.
27
27
  - Updated visual validation to confirm the admin console is separate from the
28
28
  user Brain surface.
29
- - Synchronized current-release version metadata to `4.7.1`.
29
+ - Synchronized then-current release version metadata to `4.7.1`.
30
30
  - Updated README, RELEASE.md, release notes, changelog, architecture, security,
31
31
  feature status, VS Code extension docs, and release evidence references.
32
32
 
@@ -7867,6 +7867,45 @@
7867
7867
  "summary": "Snooze Item"
7868
7868
  }
7869
7869
  },
7870
+ "/automation/reviews/{item_id}/unsnooze": {
7871
+ "post": {
7872
+ "operationId": "unsnooze_item_automation_reviews__item_id__unsnooze_post",
7873
+ "parameters": [
7874
+ {
7875
+ "in": "path",
7876
+ "name": "item_id",
7877
+ "required": true,
7878
+ "schema": {
7879
+ "title": "Item Id",
7880
+ "type": "string"
7881
+ }
7882
+ }
7883
+ ],
7884
+ "responses": {
7885
+ "200": {
7886
+ "content": {
7887
+ "application/json": {
7888
+ "schema": {
7889
+ "$ref": "#/components/schemas/ReviewItem"
7890
+ }
7891
+ }
7892
+ },
7893
+ "description": "Successful Response"
7894
+ },
7895
+ "422": {
7896
+ "content": {
7897
+ "application/json": {
7898
+ "schema": {
7899
+ "$ref": "#/components/schemas/HTTPValidationError"
7900
+ }
7901
+ }
7902
+ },
7903
+ "description": "Validation Error"
7904
+ }
7905
+ },
7906
+ "summary": "Unsnooze Item"
7907
+ }
7908
+ },
7870
7909
  "/chat": {
7871
7910
  "get": {
7872
7911
  "operationId": "chat_page_chat_get",
@@ -382,6 +382,11 @@ function BrainHome({
382
382
  </button>
383
383
  ))}
384
384
  </div>
385
+ <div className="mind-empty-trail" aria-label={t(language, "brain.empty.trail.label")}>
386
+ <span>{t(language, "brain.empty.trail.save")}</span>
387
+ <span>{t(language, "brain.empty.trail.recall")}</span>
388
+ <span>{t(language, "brain.empty.trail.backup")}</span>
389
+ </div>
385
390
  </div>
386
391
  ) : (
387
392
  messages.map((message, index) => (
@@ -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,20 +206,10 @@ function del<T>(path: string, shape: T) {
161
206
  return apiJson<T>("DELETE", path, { shape });
162
207
  }
163
208
 
164
- export type ReviewItem = {
165
- id: string;
166
- status: string;
167
- effective_status: string;
168
- title: string;
169
- summary?: string;
170
- source?: string;
171
- kind?: string;
172
- payload?: Record<string, unknown>;
173
- provenance?: Record<string, unknown>;
174
- snoozed_until?: string | null;
175
- created_at?: string | null;
176
- updated_at?: string | null;
177
- };
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";
178
213
 
179
214
  function reviewItemShape(): ReviewItem {
180
215
  return {
@@ -190,6 +225,53 @@ function reviewItemShape(): ReviewItem {
190
225
  };
191
226
  }
192
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
+
193
275
  async function uploadDocument(file: File): Promise<ApiResult<Record<string, unknown> | null>> {
194
276
  const base = await apiBase();
195
277
  const form = new FormData();
@@ -409,13 +491,12 @@ export const latticeApi = {
409
491
  hooks: () => get("/api/hooks", { hooks: [] }),
410
492
  hookRuns: () => get("/api/hooks/runs", { runs: [] }, { limit: 50 }),
411
493
  hookRun: (body: unknown) => post("/api/hooks/run", body, {}),
412
- automationReviews: (query?: { status?: string; source?: string }) =>
413
- get("/automation/reviews", { items: [] }, query),
414
- approveReviewItem: (id: string) => post(`/automation/reviews/${encodeURIComponent(id)}/approve`, {}, reviewItemShape()),
415
- dismissReviewItem: (id: string) => post(`/automation/reviews/${encodeURIComponent(id)}/dismiss`, {}, reviewItemShape()),
416
- snoozeReviewItem: (id: string, until: string) =>
417
- post(`/automation/reviews/${encodeURIComponent(id)}/snooze`, { until }, reviewItemShape()),
418
- runNowReviewItem: (id: string) => post(`/automation/reviews/${encodeURIComponent(id)}/run_now`, {}, reviewItemShape()),
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"),
419
500
  permissionsPending: () => get("/permissions/pending", { pending: {}, count: 0 }),
420
501
  approvePermission: (token: string) => post(`/permissions/approve/${encodeURIComponent(token)}`, {}, {}),
421
502
  denyPermission: (token: string) => post(`/permissions/deny/${encodeURIComponent(token)}`, {}, {}),
@@ -1914,6 +1914,23 @@ export interface paths {
1914
1914
  patch?: never;
1915
1915
  trace?: never;
1916
1916
  };
1917
+ "/automation/reviews/{item_id}/unsnooze": {
1918
+ parameters: {
1919
+ query?: never;
1920
+ header?: never;
1921
+ path?: never;
1922
+ cookie?: never;
1923
+ };
1924
+ get?: never;
1925
+ put?: never;
1926
+ /** Unsnooze Item */
1927
+ post: operations["unsnooze_item_automation_reviews__item_id__unsnooze_post"];
1928
+ delete?: never;
1929
+ options?: never;
1930
+ head?: never;
1931
+ patch?: never;
1932
+ trace?: never;
1933
+ };
1917
1934
  "/chat": {
1918
1935
  parameters: {
1919
1936
  query?: never;
@@ -11054,6 +11071,37 @@ export interface operations {
11054
11071
  };
11055
11072
  };
11056
11073
  };
11074
+ unsnooze_item_automation_reviews__item_id__unsnooze_post: {
11075
+ parameters: {
11076
+ query?: never;
11077
+ header?: never;
11078
+ path: {
11079
+ item_id: string;
11080
+ };
11081
+ cookie?: never;
11082
+ };
11083
+ requestBody?: never;
11084
+ responses: {
11085
+ /** @description Successful Response */
11086
+ 200: {
11087
+ headers: {
11088
+ [name: string]: unknown;
11089
+ };
11090
+ content: {
11091
+ "application/json": components["schemas"]["ReviewItem"];
11092
+ };
11093
+ };
11094
+ /** @description Validation Error */
11095
+ 422: {
11096
+ headers: {
11097
+ [name: string]: unknown;
11098
+ };
11099
+ content: {
11100
+ "application/json": components["schemas"]["HTTPValidationError"];
11101
+ };
11102
+ };
11103
+ };
11104
+ };
11057
11105
  chat_page_chat_get: {
11058
11106
  parameters: {
11059
11107
  query?: never;
@@ -53,10 +53,10 @@ export function FirstRunGuide() {
53
53
  <section className="arrival-panel" aria-label="First 10 minutes">
54
54
  <div className="arrival-copy">
55
55
  <div className="page-kicker"><CheckCircle2 className="h-4 w-4" /> First 10 minutes</div>
56
- <h2>Build your living Brain without guessing.</h2>
56
+ <h2>Start locally, with clear consent at each step.</h2>
57
57
  <p>
58
- Start with a local Brain, let Lattice recommend a model voice, then add the first pieces of durable knowledge.
59
- Every step keeps the next action visible.
58
+ Create the local Brain first, choose when to download a model, then add durable knowledge when you are ready.
59
+ Nothing needs cloud access unless you explicitly choose it.
60
60
  </p>
61
61
  <div className="arrival-actions">
62
62
  <Button onClick={() => go(nextStep.action)}>{nextStep.done ? "Open relationships" : `Continue: ${nextStep.label}`}</Button>
@@ -123,6 +123,10 @@ export function ProductFlow({ onComplete }: { onComplete: () => void }) {
123
123
  <RecommendationScreen
124
124
  recommendations={recommendations}
125
125
  onBack={() => setStep("analysis")}
126
+ onSkipModel={() => {
127
+ try { localStorage.setItem(FLOW_COMPLETE_KEY, "true"); } catch {}
128
+ onComplete();
129
+ }}
126
130
  onSelect={(model) => {
127
131
  setSelected(model);
128
132
  setStep("install");
@@ -333,10 +337,12 @@ function AnalysisScreen({
333
337
  function RecommendationScreen({
334
338
  recommendations,
335
339
  onBack,
340
+ onSkipModel,
336
341
  onSelect,
337
342
  }: {
338
343
  recommendations: RecommendedModel[];
339
344
  onBack: () => void;
345
+ onSkipModel: () => void;
340
346
  onSelect: (model: RecommendedModel) => void;
341
347
  }) {
342
348
  const language = useAppStore((state) => state.language);
@@ -370,6 +376,7 @@ function RecommendationScreen({
370
376
 
371
377
  <div style={{ marginTop: "1.1rem", display: "flex", justifyContent: "center", gap: "1rem", alignItems: "center" }}>
372
378
  <Button variant="ghost" onClick={onBack}>{t(language, "flow.recommend.back")}</Button>
379
+ <Button variant="outline" onClick={onSkipModel}>{t(language, "flow.recommend.skip")}</Button>
373
380
  <div style={{ fontSize: "0.82rem", color: "hsl(var(--fg-muted))" }}>{t(language, "flow.recommend.hint")}</div>
374
381
  </div>
375
382
  </div>