ltcai 3.2.0 → 3.4.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 (78) hide show
  1. package/README.md +87 -67
  2. package/docs/CHANGELOG.md +36 -0
  3. package/docs/architecture.md +2 -1
  4. package/docs/assets/v3.4.0/agent-run.png +0 -0
  5. package/docs/assets/v3.4.0/agents.png +0 -0
  6. package/docs/assets/v3.4.0/before/chat-before.png +0 -0
  7. package/docs/assets/v3.4.0/before/files-before.png +0 -0
  8. package/docs/assets/v3.4.0/chat.png +0 -0
  9. package/docs/assets/v3.4.0/connect-folder.png +0 -0
  10. package/docs/assets/v3.4.0/files.png +0 -0
  11. package/docs/assets/v3.4.0/home.png +0 -0
  12. package/docs/assets/v3.4.0/hooks-dispatch.png +0 -0
  13. package/docs/assets/v3.4.0/knowledge-graph.png +0 -0
  14. package/docs/assets/v3.4.0/local-agent.png +0 -0
  15. package/docs/assets/v3.4.0/memory.png +0 -0
  16. package/docs/assets/v3.4.0/settings.png +0 -0
  17. package/docs/assets/v3.4.0/vision-input.png +0 -0
  18. package/docs/assets/v3.4.0/workflows.png +0 -0
  19. package/knowledge_graph.py +45 -0
  20. package/knowledge_graph_api.py +10 -0
  21. package/latticeai/__init__.py +1 -1
  22. package/latticeai/api/agents.py +3 -0
  23. package/latticeai/api/hooks.py +39 -0
  24. package/latticeai/api/local_files.py +41 -0
  25. package/latticeai/api/models.py +36 -1
  26. package/latticeai/api/tools.py +16 -1
  27. package/latticeai/api/workflow_designer.py +2 -1
  28. package/latticeai/core/hooks.py +398 -2
  29. package/latticeai/core/marketplace.py +1 -1
  30. package/latticeai/core/multi_agent.py +1 -1
  31. package/latticeai/core/workflow_engine.py +21 -1
  32. package/latticeai/core/workspace_os.py +1 -1
  33. package/latticeai/server_app.py +40 -0
  34. package/latticeai/services/agent_runtime.py +46 -1
  35. package/latticeai/services/upload_service.py +17 -0
  36. package/package.json +1 -1
  37. package/scripts/build_v3_assets.mjs +7 -1
  38. package/scripts/capture/capture_v340.js +88 -0
  39. package/static/css/{tokens.5a595671.css → tokens.3ba22e37.css} +109 -109
  40. package/static/css/tokens.css +109 -109
  41. package/static/v3/asset-manifest.json +25 -25
  42. package/static/v3/css/{lattice.components.011e988b.css → lattice.components.9b49d614.css} +57 -32
  43. package/static/v3/css/lattice.components.css +57 -32
  44. package/static/v3/css/{lattice.shell.4920f42d.css → lattice.shell.6ceea7c8.css} +75 -31
  45. package/static/v3/css/lattice.shell.css +75 -31
  46. package/static/v3/css/lattice.tokens.css +13 -13
  47. package/static/v3/css/{lattice.tokens.c597ff81.css → lattice.tokens.e7018963.css} +13 -13
  48. package/static/v3/css/{lattice.views.3ee19d4e.css → lattice.views.22f69117.css} +98 -15
  49. package/static/v3/css/lattice.views.css +98 -15
  50. package/static/v3/js/{app.a5adc0f3.js → app.c4acfdd8.js} +1 -1
  51. package/static/v3/js/core/{api.603b978f.js → api.12b568ad.js} +126 -4
  52. package/static/v3/js/core/api.js +126 -4
  53. package/static/v3/js/core/{components.4c83e0a9.js → components.35f02e4c.js} +8 -0
  54. package/static/v3/js/core/components.js +8 -0
  55. package/static/v3/js/core/{routes.07ad6696.js → routes.d214b399.js} +16 -12
  56. package/static/v3/js/core/routes.js +16 -12
  57. package/static/v3/js/core/{shell.ea0b9ae5.js → shell.80a6ad82.js} +37 -9
  58. package/static/v3/js/core/shell.js +34 -6
  59. package/static/v3/js/views/agents.014d0b74.js +541 -0
  60. package/static/v3/js/views/agents.js +305 -57
  61. package/static/v3/js/views/{chat.718144ce.js → chat.e6dd7dd0.js} +162 -10
  62. package/static/v3/js/views/chat.js +162 -10
  63. package/static/v3/js/views/files.adad14c1.js +365 -0
  64. package/static/v3/js/views/files.js +269 -90
  65. package/static/v3/js/views/home.24f8b8ae.js +200 -0
  66. package/static/v3/js/views/home.js +96 -15
  67. package/static/v3/js/views/hooks.13845954.js +215 -0
  68. package/static/v3/js/views/hooks.js +117 -1
  69. package/static/v3/js/views/{memory.d2ed7a7c.js → memory.4ebdf474.js} +5 -4
  70. package/static/v3/js/views/memory.js +5 -4
  71. package/static/v3/js/views/{my-computer.1b2ff621.js → my-computer.c3ef5283.js} +224 -1
  72. package/static/v3/js/views/my-computer.js +224 -1
  73. package/static/v3/js/views/{settings.4f777210.js → settings.8631fa5e.js} +70 -2
  74. package/static/v3/js/views/settings.js +70 -2
  75. package/static/v3/js/views/agents.c373d48c.js +0 -293
  76. package/static/v3/js/views/files.4935197e.js +0 -186
  77. package/static/v3/js/views/home.cdde3b32.js +0 -119
  78. package/static/v3/js/views/hooks.f3edebca.js +0 -99
package/README.md CHANGED
@@ -16,14 +16,14 @@
16
16
  [![npm version](https://img.shields.io/npm/v/ltcai?label=npm)](https://www.npmjs.com/package/ltcai)
17
17
  [![VS Code Marketplace](https://vsmarketplacebadges.dev/version-short/parktaesoo.ltcai.svg)](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
18
18
  [![Open VSX](https://img.shields.io/open-vsx/v/parktaesoo/ltcai?label=Open%20VSX)](https://open-vsx.org/extension/parktaesoo/ltcai)
19
- [![GitHub release](https://img.shields.io/github/v/release/TaeSooPark-PTS/LatticeAI?label=GitHub%20release)](https://github.com/TaeSooPark-PTS/LatticeAI/releases/tag/v3.2.0)
19
+ [![GitHub release](https://img.shields.io/github/v/release/TaeSooPark-PTS/LatticeAI?label=GitHub%20release)](https://github.com/TaeSooPark-PTS/LatticeAI/releases)
20
20
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
21
21
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/)
22
22
  [![VS Code extension](https://img.shields.io/badge/VS%20Code-extension-blue?logo=visualstudiocode)](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
23
23
 
24
24
  </div>
25
25
 
26
- ![Lattice AI demo](docs/images/lattice-ai-demo.gif)
26
+ ![Lattice AI — local-first AI workspace](docs/assets/v3.4.0/home.png)
27
27
 
28
28
  ## Install
29
29
 
@@ -49,7 +49,7 @@ Install the coding extension:
49
49
 
50
50
  - [VS Code Marketplace: parktaesoo.ltcai](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
51
51
  - [Open VSX: parktaesoo.ltcai](https://open-vsx.org/extension/parktaesoo/ltcai)
52
- - [GitHub Release v3.2.0](https://github.com/TaeSooPark-PTS/LatticeAI/releases/tag/v3.2.0)
52
+ - [GitHub Releases](https://github.com/TaeSooPark-PTS/LatticeAI/releases)
53
53
 
54
54
  ## Quick Start
55
55
 
@@ -86,9 +86,9 @@ Lattice AI v3 is a local-first AI workspace platform for people and teams who
86
86
  want their files, models, graph context, retrieval, and agent workflows in one
87
87
  place.
88
88
 
89
- - **Primary app shell**: `/app` is the default product experience with Native
90
- Chat, Knowledge Graph, Hybrid Search, Files, Pipeline, Agents, Models, My
91
- Computer, Settings, and Admin areas. Classic pages remain compatibility
89
+ - **Primary app shell**: `/app` is the default product experience with Chat,
90
+ Files, Hybrid Search, Knowledge Graph, Memory, Models, Settings, Advanced
91
+ agent/workflow tooling, and Admin areas. Classic pages remain compatibility
92
92
  routes only; normal workflows stay in `/app`.
93
93
  - **Local-first AI Workspace**: work starts on your machine, with local data and
94
94
  workspace state by default.
@@ -122,79 +122,96 @@ and disconnected automations. Lattice AI keeps those parts together:
122
122
  - multi-agent workflows leave behind replayable plans, reviews, retries, and
123
123
  outcomes.
124
124
 
125
- ## v3.2.0 Highlights
126
-
127
- Lattice AI v3.2.0 is the feature-complete release for all non-enterprise use
128
- cases. Every platform capability is operable from `/app` with no Classic
129
- dependency built on real backends with honest live/unavailable states (never
130
- fabricated data).
131
-
132
- - **Multi-agent collaboration** Planner Researcher Executor Reviewer
133
- with handoffs, shared context packets, and replayable timelines.
134
- - **Agent Registry** every agent (built-in role + custom) carries registry
135
- metadata: type, version, capabilities, and configuration. No hardcoded lists.
136
- - **Marketplace & Templates** an offline catalog with five named agent
137
- templates (Research Assistant, Coding Assistant, Knowledge Curator,
138
- Documentation Writer, Workflow Builder) plus clone / export / import / install.
139
- - **Workflow Agents** — trigger agent chaintools → memory → result, with a
140
- run ledger and replay.
141
- - **Autonomous Planning** goal plan execute review → replan, with
142
- inspect/replay and bounded-retry safeguards.
143
- - **Long-Term Memory + Memory Manager** workspace, project, agent,
144
- conversation, graph, and vector tiers unified behind one service, with
145
- recall, inspect, prune, compact, rebuild, and clear.
146
- - **Skills, Hooks, Tool Registry, and MCP Manager** install/enable/disable
147
- skills; lifecycle hooks across runs/tools/agents/pipelines/workflows; a
148
- governed tool registry; and connected MCP servers, tools, and health.
149
-
150
- The earlier v3.1 work (Classic retired from normal workflows, retrieval wired
151
- through a shared service layer, hash-manifested assets) remains the foundation.
152
-
153
- Release audit: [docs/V3_2_AUDIT.md](docs/V3_2_AUDIT.md) records the v3.2.0
154
- claim audit, fixes, tests, real-app browser validation, and artifact checks.
155
-
156
- - Native v3 Chat lives inside `/app#/chat` and streams through the real
157
- `POST /chat` backend while showing friendly setup guidance when no model is
158
- loaded.
159
- - Knowledge Graph, Vector Index, and Hybrid Search are first-class retrieval
160
- surfaces. Hybrid results show keyword, local vector, and graph scores.
161
- - Personal and Organization workspaces, plus Basic / Advanced / Admin modes,
162
- are built into the shell.
163
- - Models can load/unload from the native Models view; Agents read the
164
- AgentRuntime boundary with run health, history, events, replay, and stop
165
- affordances.
166
- - Production embedding profiles cover local `bge-m3`, `nomic-embed-text`,
167
- `e5-large`, `gte-large`, Ollama `nomic-embed-text`, `mxbai-embed-large`,
168
- BGE-M3-compatible providers, MLX, and OpenAI-compatible
169
- `text-embedding-3-small` / `text-embedding-3-large`. Hash embeddings remain
170
- fallback only.
171
- - `/app` loads `static/v3/asset-manifest.json` and build-generated hashed
172
- assets such as `app.<hash>.js` and `lattice.base.<hash>.css`; manual `?v=`
173
- cache-busting is no longer used by runtime HTML.
125
+ ## v3.4.0 Highlights
126
+
127
+ Lattice AI v3.4.0 is the **platform completion** release: it closes the remaining
128
+ non-enterprise functionality gaps the v3.3.0 honesty audit flagged, so the
129
+ local-first workspace is complete and demonstrable end-to-end. Each item below is
130
+ runtime-verified on a live server, not only wired in source.
131
+
132
+ - **Hooks now execute.** A real dispatch engine (`run_hook` / `run_hooks` /
133
+ `fire_hook` + `HookContext` / `HookResult`) runs hooks at genuine lifecycle
134
+ pointsagents (pre/post-run), workflows (start/end), tools (pre/post-tool),
135
+ and the upload pipeline. `pre_*` hooks can gate (block) an action; every
136
+ dispatch is recorded to a persisted run log surfaced in the Hooks view.
137
+ - **Uploads appear in Files.** Uploaded documents are listed with live ingest →
138
+ index state (`/knowledge-graph/documents`), completing upload Files
139
+ Knowledge GraphHybrid SearchChat.
140
+ - **Vision (VLM) image input.** The Chat composer accepts images by attach,
141
+ drag-and-drop, or paste, with a preview and a **Vision Enabled / Disabled**
142
+ badge driven by the active model's capability.
143
+ - **Run agents from the Agents view.** A Run console (goal + roles Run / Stop /
144
+ Status / Queue / Logs) executes the multi-agent pipeline locally; it runs
145
+ without a model and fires its pre/post-run hooks.
146
+ - **On-device Local Agent + Connect Folder + Folder Watch.** My Computer reports
147
+ the real local-runtime agent status and handshake; folders can be connected and
148
+ watched (debounced reindex on change) through the existing on-device endpoints.
149
+ - **Enterprise stays honestly disabled.** SSO, SCIM, DLP, Private VPC, SIEM, and
150
+ enterprise RBAC remain off with honest "not available in this build" states.
151
+
152
+ See [RELEASE_NOTES_v3.4.0.md](RELEASE_NOTES_v3.4.0.md),
153
+ [PLATFORM_COMPLETION_REPORT_v3.4.0.md](PLATFORM_COMPLETION_REPORT_v3.4.0.md), and
154
+ the evidence-traced [FEATURE_STATUS.md](FEATURE_STATUS.md).
155
+
156
+ ## v3.3.1 Highlights
157
+
158
+ Lattice AI v3.3.1 rebuilds the visible `/app` product experience while
159
+ preserving the existing local-first runtime. The app now presents Chat, Files,
160
+ Search, Knowledge, Memory, Models, Settings, Advanced tooling, and Admin
161
+ workflows with clearer navigation and honest live/unavailable states.
162
+
163
+ - **Visual product rebuild** compact rail navigation, quieter topbar,
164
+ command-palette search, retrieval readiness footer, and denser controls.
165
+ - **Truthful Home dashboard** — backend, model, retrieval, memory, source, and
166
+ trace readiness are derived from real endpoints instead of fabricated counts.
167
+ - **Basic / Advanced / Admin navigation** — Basic focuses on core workspace
168
+ workflows; Advanced exposes agents, workflows, skills, hooks, and MCP; Admin
169
+ keeps organization controls separate.
170
+ - **Files and Settings clarity** — manual upload is available immediately,
171
+ folder watching is explicitly tied to the desktop local agent, and Settings
172
+ shows backend, agent, model, telemetry, and embedding readiness.
173
+ - **Design system refresh** cooler neutral light/dark tokens, tighter 8px
174
+ radius discipline, compact cards/tables/stats/buttons, and regenerated
175
+ hashed v3 assets.
176
+
177
+ The v3.2.0 platform remains the feature-complete foundation: multi-agent
178
+ collaboration, Agent Registry, Marketplace templates, Workflow Agents,
179
+ Autonomous Planning, Long-Term Memory, Skills, Hooks, Tool Registry, MCP
180
+ Manager, production embedding profiles, and hash-manifested `/app` assets.
181
+ Release audit: [docs/V3_2_AUDIT.md](docs/V3_2_AUDIT.md).
174
182
 
175
183
  ## Screenshots
176
184
 
177
- ### Workspace
185
+ All screenshots are the v3.4.0 `/app` shell. Live model output (VLM inference,
186
+ agent-generated text) requires a loaded local model and is not depicted.
178
187
 
179
- ![Workspace light theme](docs/images/workspace-light.png)
188
+ ### Home
180
189
 
181
- ![Workspace dark theme](docs/images/workspace-dark.png)
190
+ ![Home local-first workspace at a glance](docs/assets/v3.4.0/home.png)
182
191
 
183
- ### Knowledge Graph
192
+ ### Chat with Vision (VLM) image input
193
+
194
+ ![Chat — image attach + Vision Enabled badge](docs/assets/v3.4.0/chat.png)
195
+
196
+ ### Files — uploaded documents + Connect Folder
184
197
 
185
- ![Knowledge Graph](docs/images/knowledge-graph.png)
198
+ ![Files — uploaded documents with index state](docs/assets/v3.4.0/files.png)
186
199
 
187
- ### AI Pipeline
200
+ ### Run agents from the Agents view
188
201
 
189
- ![AI Pipeline designer](docs/images/pipeline.png)
202
+ ![Agent run — goal, roles, and live timeline logs](docs/assets/v3.4.0/agent-run.png)
190
203
 
191
- ### Admin Dashboard
204
+ ### Hooks dispatch + run log
192
205
 
193
- ![Admin dashboard](docs/images/admin-dashboard.png)
206
+ ![Hooks — per-hook Run and recent executions](docs/assets/v3.4.0/hooks-dispatch.png)
194
207
 
195
- ### Mobile Responsive
208
+ ### Local Agent (on-device runtime)
209
+
210
+ ![My Computer — Local Agent status and handshake](docs/assets/v3.4.0/local-agent.png)
211
+
212
+ ### Knowledge Graph
196
213
 
197
- ![Mobile responsive layout](docs/images/mobile-responsive.png)
214
+ ![Knowledge Graph](docs/assets/v3.4.0/knowledge-graph.png)
198
215
 
199
216
  ## Knowledge Graph Flow
200
217
 
@@ -290,7 +307,10 @@ Core areas:
290
307
 
291
308
  | Version | Theme |
292
309
  | --- | --- |
293
- | **3.2.0** | Feature-complete platformmulti-agent collaboration, agent registry, marketplace + templates, workflow agents, autonomous planning, long-term memory + manager, skills/hooks/tool registries, MCP manager, all operable from `/app` |
310
+ | **3.4.0** | Platform completionhooks execution engine, uploads visible in Files, VLM image input, agent run trigger, on-device Local Agent / Connect Folder / Folder Watch; Enterprise stays honestly disabled; refreshed v3.4.0 public assets |
311
+ | 3.3.1 | Visual product rebuild — rebuilt `/app` shell, Basic/Advanced/Admin navigation, cooler token palette, compact component system, Home readiness dashboard, Files local-agent truthfulness, Settings runtime status, and v3.3.1 design notes |
312
+ | **3.3.0** | Product quality & honesty release — evidence-based feature audit (`FEATURE_STATUS.md`), single-source version truth, working manual document upload in Files, fixed document-generation streaming, truthful Home retrieval status, documented design system (`STYLE_SYSTEM.md`) |
313
+ | 3.2.0 | Feature-complete platform — multi-agent collaboration, agent registry, marketplace + templates, workflow agents, autonomous planning, long-term memory + manager, skills/hooks/tool registries, MCP manager, all operable from `/app` |
294
314
  | 3.1.0 | Mainline platform completion — native `/app` workflows, Classic retired from normal paths, production embedding profiles, AgentRuntime/registries, hashed v3 assets |
295
315
  | 3.0.1 | Release-blocker remediation — provider-backed embeddings (Hash/MLX/Ollama/OpenAI/Custom), unified AgentRuntime boundary, every v3 surface connected or clearly unavailable |
296
316
  | 3.0.0 | v3 local-first AI workspace platform — `/app`, Native Chat, Knowledge Graph, Vector Index, Hybrid Search, workspace modes |
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.1] - 2026-06-08
4
+
5
+ > v3.3.1 — Visual Product Rebuild. The `/app` frontend keeps the same runtime
6
+ > behavior but receives a new product shell, navigation hierarchy, visual token
7
+ > system, and readiness-focused primary views.
8
+
9
+ ### Changed
10
+
11
+ - Rebuilt the global `/app` shell with a denser command rail, grouped
12
+ Basic/Advanced/Admin navigation, local retrieval readiness footer, quiet
13
+ topbar, and mode-aware command palette.
14
+ - Reorganized production navigation to Basic (Home, Chat, Files, Search,
15
+ Knowledge, Memory, Models, Settings), Advanced (Agents, Workflows, Skills,
16
+ Hooks, MCP), and Admin (Users, Permissions, Audit Logs, Security, Policies,
17
+ Private VPC). Compatibility-only views remain deep-linkable.
18
+ - Replaced the v3.3.0 palette with cooler neutral light/dark tokens, tightened
19
+ cards/panels to an 8px radius system, and compacted buttons, inputs, stats,
20
+ tables, segmented controls, and empty states.
21
+ - Rebuilt Home as a local readiness dashboard for backend, model, retrieval,
22
+ memory, connected sources, stats, and recent activity when available.
23
+ - Clarified Files manual upload versus desktop local-agent folder connection.
24
+ - Added Settings runtime readiness for backend, desktop local agent, model
25
+ runtime, host telemetry, and embedding provider configuration.
26
+ - Fixed Chat send/stop button wiring so streaming uses a stable handler.
27
+
28
+ ### Added
29
+
30
+ - `VISUAL_REBUILD_NOTES_v3.3.1.md` with implementation and QA notes.
31
+ - `FIGMA_SPEC.md` as the in-repo Figma-equivalent design spec for v3.3.1.
32
+
33
+ ### Validation
34
+
35
+ - `npm run build:assets` regenerated content-hashed v3 assets at `3.3.1`.
36
+ - Package publication, deployment, tags, and GitHub Release creation were not
37
+ performed.
38
+
3
39
  ## [3.2.0] - 2026-06-08
4
40
 
5
41
  > v3.2 — Feature-Complete Platform. Multi-agent collaboration, an agent
@@ -1,6 +1,7 @@
1
1
  # Lattice AI Architecture
2
2
 
3
- > v3.2.0 — feature-complete for non-enterprise use cases. The agent ecosystem
3
+ > v3.3.1 — feature-complete for non-enterprise use cases with a rebuilt `/app`
4
+ > visual shell. The agent ecosystem
4
5
  > (registry, marketplace + templates, workflow agents, autonomous planning),
5
6
  > the long-term memory platform + manager, and the skills/hooks/tool/MCP
6
7
  > registries are all operable from `/app`. Enterprise controls remain future
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -3046,6 +3046,51 @@ class KnowledgeGraphStore:
3046
3046
  "Decision", # 결정 사항
3047
3047
  )
3048
3048
 
3049
+ def list_documents(self, limit: int = 200) -> Dict[str, Any]:
3050
+ """List ingested ``Document`` nodes with their ingest + index state.
3051
+
3052
+ Powers the Files view: every accepted upload and every indexed local
3053
+ document becomes a ``Document`` node. A document is reported ``indexed``
3054
+ once its retrieval chunks exist (searchable in Chat / Hybrid Search).
3055
+ """
3056
+ limit = max(1, min(int(limit or 200), 1000))
3057
+ nt, _ = self._read_tables()
3058
+ documents: List[Dict[str, Any]] = []
3059
+ with self._connect() as conn:
3060
+ rows = conn.execute(
3061
+ f"SELECT id, title, summary, metadata_json, created_at, updated_at "
3062
+ f"FROM {nt} WHERE type='Document' ORDER BY updated_at DESC, id ASC LIMIT ?",
3063
+ (limit,),
3064
+ ).fetchall()
3065
+ for row in rows:
3066
+ meta = _safe_loads(row["metadata_json"]) or {}
3067
+ extracted = meta.get("extracted") or {}
3068
+ node_id = row["id"]
3069
+ chunk_count = conn.execute(
3070
+ f"SELECT COUNT(*) AS c FROM {nt} WHERE type='Chunk' AND metadata_json LIKE ?",
3071
+ (f"%{node_id}%",),
3072
+ ).fetchone()["c"]
3073
+ documents.append({
3074
+ "id": node_id,
3075
+ "filename": meta.get("filename") or row["title"],
3076
+ "ext": meta.get("ext"),
3077
+ "mime_type": meta.get("mime_type"),
3078
+ "bytes": meta.get("bytes"),
3079
+ "sha256": meta.get("sha256"),
3080
+ "uploader": meta.get("uploader"),
3081
+ "chars": extracted.get("chars"),
3082
+ "chunks": int(chunk_count or 0),
3083
+ "indexed": int(chunk_count or 0) > 0,
3084
+ "ingest_state": "indexed" if int(chunk_count or 0) > 0 else "ingested",
3085
+ "created_at": row["created_at"],
3086
+ "updated_at": row["updated_at"],
3087
+ })
3088
+ return {
3089
+ "documents": documents,
3090
+ "total": len(documents),
3091
+ "generated_at": datetime.now().isoformat(timespec="seconds"),
3092
+ }
3093
+
3049
3094
  def graph(self, limit: int = 300) -> Dict[str, Any]:
3050
3095
  limit = max(1, min(int(limit or 300), 2000))
3051
3096
  visible = ",".join(f"'{t}'" for t in self._GRAPH_VISIBLE_TYPES)
@@ -75,6 +75,16 @@ def create_knowledge_graph_router(
75
75
  require_user(request)
76
76
  return graph().graph(limit)
77
77
 
78
+ @router.get("/knowledge-graph/documents")
79
+ async def knowledge_graph_documents(request: Request, limit: int = 200):
80
+ """Ingested documents (uploads + indexed local docs) with index state.
81
+
82
+ Backs the Files view so uploaded content is visible end-to-end:
83
+ upload → Files → Knowledge Graph → Hybrid Search → Chat.
84
+ """
85
+ require_user(request)
86
+ return graph().list_documents(limit)
87
+
78
88
  @router.get("/knowledge-graph/search")
79
89
  async def knowledge_graph_search(q: str, request: Request, limit: int = 30):
80
90
  require_user(request)
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "3.2.0"
3
+ __version__ = "3.4.0"
@@ -173,5 +173,8 @@ def create_agents_router(
173
173
  )
174
174
  except ValueError as exc:
175
175
  raise HTTPException(status_code=400, detail=str(exc)) from exc
176
+ except PermissionError as exc:
177
+ # A pre_run hook gated this run (e.g. a policy/permission hook).
178
+ raise HTTPException(status_code=403, detail=str(exc)) from exc
176
179
 
177
180
  return router
@@ -35,6 +35,13 @@ class HookRegisterRequest(BaseModel):
35
35
  enabled: bool = True
36
36
 
37
37
 
38
+ class HookRunRequest(BaseModel):
39
+ kind: Optional[str] = None
40
+ hook_id: Optional[str] = None
41
+ event: str = ""
42
+ payload: dict = {}
43
+
44
+
38
45
  def create_hooks_router(
39
46
  *,
40
47
  registry: HooksRegistry,
@@ -48,6 +55,38 @@ def create_hooks_router(
48
55
  require_user(request)
49
56
  return registry.list(kind=kind)
50
57
 
58
+ # NOTE: declared before the ``/{hook_id:path}`` catch-all so "runs" is not
59
+ # captured as a hook id.
60
+ @router.get("/api/hooks/runs")
61
+ async def hook_runs(request: Request, limit: int = 50, kind: Optional[str] = None):
62
+ require_user(request)
63
+ return registry.recent_runs(limit=limit, kind=kind)
64
+
65
+ @router.post("/api/hooks/run")
66
+ async def run_hooks(req: HookRunRequest, request: Request):
67
+ """Execute hooks now — by ``kind`` (all enabled hooks of that kind) or a
68
+ single ``hook_id``. Returns the dispatch record so callers can see what
69
+ ran, in what order, and whether the action was blocked."""
70
+ user = require_user(request)
71
+ try:
72
+ if req.hook_id:
73
+ result = registry.run_hook(req.hook_id, event=req.event or None, payload=req.payload, user_email=user)
74
+ elif req.kind:
75
+ result = registry.run_hooks(req.kind, event=req.event or None, payload=req.payload, user_email=user)
76
+ else:
77
+ raise HTTPException(status_code=400, detail="Provide a 'kind' or a 'hook_id' to run.")
78
+ except KeyError as exc:
79
+ raise HTTPException(status_code=404, detail=f"Hook not found: {exc}") from exc
80
+ except ValueError as exc:
81
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
82
+ append_audit_event("hook_run", user_email=user, hook_id=req.hook_id, kind=req.kind, event=req.event)
83
+ return result
84
+
85
+ # Alias for the same dispatch action (fire == run).
86
+ @router.post("/api/hooks/fire")
87
+ async def fire_hooks(req: HookRunRequest, request: Request):
88
+ return await run_hooks(req, request)
89
+
51
90
  @router.get("/api/hooks/{hook_id:path}")
52
91
  async def inspect_hook(hook_id: str, request: Request):
53
92
  require_user(request)
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import platform
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
 
@@ -39,6 +40,46 @@ def create_local_files_router(
39
40
  ) -> APIRouter:
40
41
  router = APIRouter()
41
42
 
43
+ @router.get("/api/local-agent/status")
44
+ async def local_agent_status(request: Request):
45
+ """Real on-device runtime status — the 'Local Agent' is the Lattice
46
+ server running on this machine (filesystem access + folder watching +
47
+ local inference), not a separate desktop process. Everything reported
48
+ here is observed, never faked."""
49
+ require_user(request)
50
+ watch = local_kg_watcher.status() if local_kg_watcher else {"available": False, "active": {}}
51
+ sources = []
52
+ try:
53
+ if knowledge_graph is not None:
54
+ sources = (knowledge_graph.local_sources() or {}).get("sources", [])
55
+ except Exception:
56
+ sources = []
57
+ watched = len(watch.get("active", {}) or {})
58
+ return {
59
+ "agent": {
60
+ "id": "lattice-local-runtime",
61
+ "name": "Lattice Local Agent",
62
+ "kind": "on-device-runtime",
63
+ "online": True,
64
+ "platform": platform.platform(),
65
+ "machine": platform.machine(),
66
+ "python": platform.python_version(),
67
+ },
68
+ "handshake": {
69
+ "ok": True,
70
+ "transport": "in-process",
71
+ "detail": "The local Lattice runtime serves this workspace on-device; no external agent is required.",
72
+ },
73
+ "health": {
74
+ "status": "ok",
75
+ "filesystem_access": True,
76
+ "watcher_available": bool(watch.get("available")),
77
+ },
78
+ "folders": {"connected": len(sources), "watching": watched},
79
+ "watch": watch,
80
+ "sources": sources,
81
+ }
82
+
42
83
  @router.post("/local/list")
43
84
  async def local_list_endpoint(req: LocalAccessRequest, request: Request):
44
85
  current_user = permission_gateway.require_local_user(request)
@@ -23,6 +23,39 @@ from fastapi.responses import StreamingResponse
23
23
  from pydantic import BaseModel
24
24
 
25
25
 
26
+ def _vision_capability(current_model_id: Optional[str], engines: Any) -> Dict[str, Any]:
27
+ """Whether the active model can accept image input (VLM).
28
+
29
+ Honest, derived signal for the Chat 'Vision Enabled / Disabled' badge: a
30
+ model is vision-capable only when its compat profile reports
31
+ ``supports_vision``. The MLX-VLM engine availability is reported too so the
32
+ UI can explain a disabled badge ("load a vision model" vs "install MLX-VLM").
33
+ """
34
+ from latticeai.core.model_compat import get_model_profile
35
+
36
+ current_vision = False
37
+ if current_model_id:
38
+ try:
39
+ current_vision = bool(get_model_profile(current_model_id).get("supports_vision"))
40
+ except Exception:
41
+ current_vision = False
42
+ engine_available = False
43
+ try:
44
+ for eng in (engines or []):
45
+ if isinstance(eng, dict) and eng.get("id") in {"local_mlx", "mlx"} and eng.get("installed"):
46
+ engine_available = True
47
+ break
48
+ except Exception:
49
+ engine_available = False
50
+ return {
51
+ "current_model": current_model_id,
52
+ "current_supports_vision": current_vision,
53
+ "engine_available": engine_available,
54
+ # The badge is "enabled" only when a vision-capable model is active.
55
+ "enabled": bool(current_vision),
56
+ }
57
+
58
+
26
59
  class LoadModelRequest(BaseModel):
27
60
  model_id: str
28
61
  engine: Optional[str] = None
@@ -257,13 +290,15 @@ def create_models_router(
257
290
  recommended = _recommended_with_engine_options(
258
291
  list(filter_lower_family_versions(ENGINE_MODEL_CATALOG.get("local_mlx", [])))
259
292
  )
293
+ engines = await asyncio.to_thread(engine_status)
260
294
  return {
261
295
  "recommended": recommended,
262
296
  "cloud": _router.detected_cloud_models(),
263
- "engines": await asyncio.to_thread(engine_status),
297
+ "engines": engines,
264
298
  "loaded": _router.loaded_model_ids,
265
299
  "current": _router.current_model_id,
266
300
  "compat_profiles": _list_compat_profiles(),
301
+ "vision": _vision_capability(_router.current_model_id, engines),
267
302
  }
268
303
 
269
304
  @router.get("/models/compat-profiles")
@@ -197,8 +197,10 @@ def create_tools_router(
197
197
  recommend_mcps,
198
198
  install_mcp,
199
199
  mcp_public_item,
200
+ hooks=None,
200
201
  ) -> APIRouter:
201
202
  api_router = APIRouter()
203
+ HOOKS = hooks
202
204
  CONFIG = config
203
205
  DATA_DIR = data_dir
204
206
  STATIC_DIR = static_dir
@@ -219,10 +221,22 @@ def create_tools_router(
219
221
  # ── Direct Tool API ───────────────────────────────────────────────────────────
220
222
 
221
223
  def _tool_response(fn, *args):
224
+ tool_name = getattr(fn, "__name__", "tool")
225
+ # ── pre_tool hooks ── a blocking pre_tool hook (e.g. a permission gate)
226
+ # stops the tool call before it runs.
227
+ if HOOKS is not None:
228
+ pre = HOOKS.fire_hook("pre_tool", f"tool.{tool_name}", payload={"tool": tool_name, "argc": len(args)})
229
+ if pre.get("blocked"):
230
+ raise HTTPException(status_code=403, detail=pre.get("block_reason") or f"Tool '{tool_name}' blocked by a pre_tool hook.")
222
231
  try:
223
- return {"status": "ok", "workspace": str(AGENT_ROOT), "result": fn(*args)}
232
+ result = fn(*args)
224
233
  except ToolError as exc:
234
+ if HOOKS is not None:
235
+ HOOKS.fire_hook("post_tool", f"tool.{tool_name}", payload={"tool": tool_name, "status": "error", "detail": str(exc)})
225
236
  raise HTTPException(status_code=400, detail=str(exc))
237
+ if HOOKS is not None:
238
+ HOOKS.fire_hook("post_tool", f"tool.{tool_name}", payload={"tool": tool_name, "status": "ok"})
239
+ return {"status": "ok", "workspace": str(AGENT_ROOT), "result": result}
226
240
 
227
241
 
228
242
  @api_router.post("/tools/list_dir")
@@ -431,6 +445,7 @@ def create_tools_router(
431
445
  classify_sensitive_message=classify_sensitive_message,
432
446
  append_audit_event=append_audit_event,
433
447
  enforce_rate_limit=enforce_rate_limit,
448
+ hooks=HOOKS,
434
449
  )
435
450
 
436
451
 
@@ -57,6 +57,7 @@ def create_workflow_designer_router(
57
57
  append_audit_event: Callable[..., None],
58
58
  ui_file_response: Optional[Callable[[Path], Any]] = None,
59
59
  static_dir: Optional[Path] = None,
60
+ hooks: Any = None,
60
61
  ) -> APIRouter:
61
62
  from latticeai.core.workflow_engine import (
62
63
  WorkflowEngine,
@@ -147,7 +148,7 @@ def create_workflow_designer_router(
147
148
  except FileNotFoundError as exc:
148
149
  raise HTTPException(status_code=404, detail=f"Workflow not found: {exc}") from exc
149
150
  runners = build_runners(current_user or None, scope)
150
- engine = WorkflowEngine(runners)
151
+ engine = WorkflowEngine(runners, hooks=hooks)
151
152
  result = engine.run(workflow, inputs=req.inputs)
152
153
  run = store.record_workflow_run(
153
154
  workflow_id=workflow_id,