ltcai 3.0.1 → 3.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.
- package/README.md +54 -21
- package/docs/CHANGELOG.md +90 -0
- package/docs/V3_2_AUDIT.md +82 -0
- package/docs/V3_FRONTEND.md +20 -17
- package/docs/architecture.md +6 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_registry.py +103 -0
- package/latticeai/api/auth.py +4 -1
- package/latticeai/api/hooks.py +113 -0
- package/latticeai/api/marketplace.py +13 -0
- package/latticeai/api/memory.py +109 -0
- package/latticeai/api/search.py +4 -0
- package/latticeai/core/agent_registry.py +234 -0
- package/latticeai/core/config.py +2 -0
- package/latticeai/core/embedding_providers.py +123 -0
- package/latticeai/core/hooks.py +284 -0
- package/latticeai/core/marketplace.py +87 -2
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/server_app.py +63 -6
- package/latticeai/services/memory_service.py +324 -0
- package/package.json +9 -4
- package/scripts/build_v3_assets.mjs +164 -0
- package/scripts/capture/README.md +28 -0
- package/scripts/capture/capture_enterprise.js +8 -0
- package/scripts/capture/capture_graph.js +8 -0
- package/scripts/capture/capture_onboarding.js +8 -0
- package/scripts/capture/capture_page.js +43 -0
- package/scripts/capture/capture_release_media.js +125 -0
- package/scripts/capture/capture_skills.js +8 -0
- package/scripts/capture/capture_workspace.js +8 -0
- package/scripts/generate_diagrams.py +513 -0
- package/scripts/lint_v3.mjs +33 -0
- package/scripts/release-0.3.1.sh +105 -0
- package/scripts/take_screenshots.js +69 -0
- package/scripts/validate_release_artifacts.py +167 -0
- package/static/account.html +9 -9
- package/static/activity.html +4 -4
- package/static/admin.html +8 -8
- package/static/agents.html +4 -4
- package/static/chat.html +9 -9
- package/static/css/tokens.5a595671.css +260 -0
- package/static/css/tokens.css +1 -1
- package/static/graph.html +9 -9
- package/static/plugins.html +4 -4
- package/static/sw.js +3 -1
- package/static/v3/asset-manifest.json +55 -0
- package/static/v3/css/lattice.base.e4cdd05d.css +128 -0
- package/static/v3/css/lattice.components.011e988b.css +447 -0
- package/static/v3/css/lattice.components.css +2 -2
- package/static/v3/css/lattice.shell.4920f42d.css +407 -0
- package/static/v3/css/lattice.tokens.c597ff81.css +132 -0
- package/static/v3/css/lattice.views.3ee19d4e.css +277 -0
- package/static/v3/index.html +38 -9
- package/static/v3/js/app.a5adc0f3.js +26 -0
- package/static/v3/js/core/api.603b978f.js +408 -0
- package/static/v3/js/core/api.js +132 -51
- package/static/v3/js/core/components.4c83e0a9.js +222 -0
- package/static/v3/js/core/components.js +9 -2
- package/static/v3/js/core/dom.a2773eb0.js +148 -0
- package/static/v3/js/core/router.584570f2.js +37 -0
- package/static/v3/js/core/routes.07ad6696.js +89 -0
- package/static/v3/js/core/routes.js +17 -1
- package/static/v3/js/core/shell.ea0b9ae5.js +363 -0
- package/static/v3/js/core/store.34ebd5e6.js +113 -0
- package/static/v3/js/views/admin-audit.660a1fb1.js +185 -0
- package/static/v3/js/views/admin-audit.js +1 -1
- package/static/v3/js/views/admin-permissions.a7ae5f09.js +177 -0
- package/static/v3/js/views/admin-permissions.js +4 -5
- package/static/v3/js/views/admin-policies.3658fd86.js +102 -0
- package/static/v3/js/views/admin-policies.js +4 -5
- package/static/v3/js/views/admin-private-vpc.7d342d36.js +135 -0
- package/static/v3/js/views/admin-private-vpc.js +2 -5
- package/static/v3/js/views/admin-security.07c66b72.js +180 -0
- package/static/v3/js/views/admin-security.js +4 -5
- package/static/v3/js/views/admin-users.03bac88c.js +168 -0
- package/static/v3/js/views/admin-users.js +6 -6
- package/static/v3/js/views/agents.c373d48c.js +293 -0
- package/static/v3/js/views/agents.js +101 -2
- package/static/v3/js/views/chat.718144ce.js +449 -0
- package/static/v3/js/views/chat.js +2 -3
- package/static/v3/js/views/files.4935197e.js +186 -0
- package/static/v3/js/views/files.js +27 -21
- package/static/v3/js/views/home.cdde3b32.js +119 -0
- package/static/v3/js/views/hooks.f3edebca.js +99 -0
- package/static/v3/js/views/hooks.js +99 -0
- package/static/v3/js/views/hybrid-search.b22b97e0.js +195 -0
- package/static/v3/js/views/hybrid-search.js +1 -1
- package/static/v3/js/views/knowledge-graph.a14ea7e7.js +237 -0
- package/static/v3/js/views/knowledge-graph.js +2 -3
- package/static/v3/js/views/marketplace.ab0583d4.js +141 -0
- package/static/v3/js/views/marketplace.js +141 -0
- package/static/v3/js/views/mcp.99b5c6a7.js +114 -0
- package/static/v3/js/views/mcp.js +114 -0
- package/static/v3/js/views/memory.d2ed7a7c.js +146 -0
- package/static/v3/js/views/memory.js +146 -0
- package/static/v3/js/views/models.a1ffa147.js +256 -0
- package/static/v3/js/views/models.js +17 -8
- package/static/v3/js/views/my-computer.1b2ff621.js +237 -0
- package/static/v3/js/views/my-computer.js +5 -5
- package/static/v3/js/views/pipeline.c522f1ce.js +157 -0
- package/static/v3/js/views/pipeline.js +3 -7
- package/static/v3/js/views/planning.9ac3e313.js +153 -0
- package/static/v3/js/views/planning.js +153 -0
- package/static/v3/js/views/settings.4f777210.js +250 -0
- package/static/v3/js/views/settings.js +6 -14
- package/static/v3/js/views/skills.c6c2f965.js +109 -0
- package/static/v3/js/views/skills.js +109 -0
- package/static/v3/js/views/tools.e4f11276.js +108 -0
- package/static/v3/js/views/tools.js +108 -0
- package/static/v3/js/views/workflows.26c57290.js +128 -0
- package/static/v3/js/views/workflows.js +128 -0
- package/static/workflows.html +4 -4
- package/static/workspace.html +5 -5
- package/docs/images/tmp_frames/frame_00.png +0 -0
- package/docs/images/tmp_frames/frame_01.png +0 -0
- package/docs/images/tmp_frames/frame_02.png +0 -0
- package/docs/images/tmp_frames/frame_03.png +0 -0
- package/docs/images/tmp_frames/hero_00.png +0 -0
- package/docs/images/tmp_frames/hero_01.png +0 -0
- package/docs/images/tmp_frames/hero_02.png +0 -0
- package/docs/images/tmp_frames/hero_03.png +0 -0
- package/static/v3/js/core/fixtures.js +0 -171
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
[](https://www.npmjs.com/package/ltcai)
|
|
17
17
|
[](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
|
|
18
18
|
[](https://open-vsx.org/extension/parktaesoo/ltcai)
|
|
19
|
-
[](https://github.com/TaeSooPark-PTS/LatticeAI/releases/tag/v3.
|
|
19
|
+
[](https://github.com/TaeSooPark-PTS/LatticeAI/releases/tag/v3.2.0)
|
|
20
20
|
[](LICENSE)
|
|
21
21
|
[](https://www.python.org/)
|
|
22
22
|
[](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
|
|
@@ -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.
|
|
52
|
+
- [GitHub Release v3.2.0](https://github.com/TaeSooPark-PTS/LatticeAI/releases/tag/v3.2.0)
|
|
53
53
|
|
|
54
54
|
## Quick Start
|
|
55
55
|
|
|
@@ -88,8 +88,8 @@ place.
|
|
|
88
88
|
|
|
89
89
|
- **Primary app shell**: `/app` is the default product experience with Native
|
|
90
90
|
Chat, Knowledge Graph, Hybrid Search, Files, Pipeline, Agents, Models, My
|
|
91
|
-
Computer, Settings, and Admin areas.
|
|
92
|
-
|
|
91
|
+
Computer, Settings, and Admin areas. Classic pages remain compatibility
|
|
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.
|
|
95
95
|
- **AI Pipeline Platform**: plan, execute, review, retry, and replay work across
|
|
@@ -105,8 +105,8 @@ place.
|
|
|
105
105
|
Knowledge Graph and fused with keyword and graph signals.
|
|
106
106
|
- **Local Model Management**: choose current multimodal local models with source
|
|
107
107
|
disclosure, hardware-aware recommendations, and cloud fallback options.
|
|
108
|
-
- **
|
|
109
|
-
|
|
108
|
+
- **Community-first workspaces**: Personal and Organization workspaces ship in
|
|
109
|
+
the local product; enterprise SSO/SCIM/governance remain future extensions.
|
|
110
110
|
|
|
111
111
|
## Why Lattice AI?
|
|
112
112
|
|
|
@@ -122,10 +122,36 @@ 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.
|
|
126
|
-
|
|
127
|
-
Lattice AI v3.
|
|
128
|
-
|
|
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 chain → tools → 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.
|
|
129
155
|
|
|
130
156
|
- Native v3 Chat lives inside `/app#/chat` and streams through the real
|
|
131
157
|
`POST /chat` backend while showing friendly setup guidance when no model is
|
|
@@ -134,11 +160,17 @@ backend retrieval stack together with the native frontend.
|
|
|
134
160
|
surfaces. Hybrid results show keyword, local vector, and graph scores.
|
|
135
161
|
- Personal and Organization workspaces, plus Basic / Advanced / Admin modes,
|
|
136
162
|
are built into the shell.
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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.
|
|
142
174
|
|
|
143
175
|
## Screenshots
|
|
144
176
|
|
|
@@ -184,11 +216,10 @@ The v3 backend adds a local-first retrieval stack that combines the Knowledge
|
|
|
184
216
|
Graph, a SQLite vector index, and hybrid result fusion. It preserves existing
|
|
185
217
|
graph data while adding derived vector rows that can be rebuilt at any time.
|
|
186
218
|
|
|
187
|
-
Embedding status:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
semantic embedding model.
|
|
191
|
-
OpenAI-compatible providers, and other local embedding runtimes.
|
|
219
|
+
Embedding status: production profiles are exposed through
|
|
220
|
+
`GET /api/embeddings/providers`, while `lattice-local-hash-v1` remains a
|
|
221
|
+
deterministic fallback for offline indexing and tests. It is never presented as
|
|
222
|
+
a production semantic embedding model.
|
|
192
223
|
|
|
193
224
|
Core API contracts:
|
|
194
225
|
|
|
@@ -259,7 +290,9 @@ Core areas:
|
|
|
259
290
|
|
|
260
291
|
| Version | Theme |
|
|
261
292
|
| --- | --- |
|
|
262
|
-
| **3.0
|
|
293
|
+
| **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
|
+
| 3.1.0 | Mainline platform completion — native `/app` workflows, Classic retired from normal paths, production embedding profiles, AgentRuntime/registries, hashed v3 assets |
|
|
295
|
+
| 3.0.1 | Release-blocker remediation — provider-backed embeddings (Hash/MLX/Ollama/OpenAI/Custom), unified AgentRuntime boundary, every v3 surface connected or clearly unavailable |
|
|
263
296
|
| 3.0.0 | v3 local-first AI workspace platform — `/app`, Native Chat, Knowledge Graph, Vector Index, Hybrid Search, workspace modes |
|
|
264
297
|
| 2.2.7 | Visual system stabilization — cohesive dark/light screens, crisp chat composer, dark graph canvas, Workspace OS polish |
|
|
265
298
|
| 2.2.6 | Token-native CSS foundation |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,95 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.2.0] - 2026-06-08
|
|
4
|
+
|
|
5
|
+
> v3.2 — Feature-Complete Platform. Multi-agent collaboration, an agent
|
|
6
|
+
> registry, marketplace + templates, workflow agents, autonomous planning, a
|
|
7
|
+
> long-term memory platform + manager, and skills/hooks/tool/MCP registries are
|
|
8
|
+
> all operable from `/app`. Enterprise (SSO/SCIM/RBAC/compliance/DLP/VPC/
|
|
9
|
+
> governance/multi-tenant controls) remains future work.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Agent Registry** — `latticeai/core/agent_registry.py` +
|
|
14
|
+
`/agents/api/registry*`: built-in roles projected from `multi_agent`,
|
|
15
|
+
persisted custom agents, capability discovery, and per-agent config.
|
|
16
|
+
- **Hooks platform** — `latticeai/core/hooks.py` + `/api/hooks/*`: persisted
|
|
17
|
+
lifecycle registry (pre_run/post_run/pre_tool/post_tool/agent/pipeline/
|
|
18
|
+
workflow) with enable/disable/reorder/register/inspect.
|
|
19
|
+
- **Long-term memory platform + Memory Manager** —
|
|
20
|
+
`latticeai/services/memory_service.py` + `/api/memory/*`: unifies workspace
|
|
21
|
+
memories, agent snapshots, conversation history, and KG graph/vector behind
|
|
22
|
+
one façade; recall/inspect/prune/compact/rebuild/clear + usage/sources/health.
|
|
23
|
+
- **Agent templates** — five named templates in `latticeai/core/marketplace.py`
|
|
24
|
+
plus a `clone` endpoint; `MARKETPLACE_VERSION`/`PLUGIN_SDK_VERSION`/
|
|
25
|
+
`MULTI_AGENT_VERSION` → `3.2.0`.
|
|
26
|
+
- **MCP Manager surface** — `create_mcp_router` mounted through the tools router
|
|
27
|
+
in `server_app`, reviving `/mcp/*`, `/skills/marketplace`,
|
|
28
|
+
`/plugins/directory`, `/mcp/call`.
|
|
29
|
+
- **Eight `/app` views** — memory, planning, workflows, marketplace, skills,
|
|
30
|
+
hooks, tools, mcp; a Platform nav group; fallback-safe `api.js` adapters.
|
|
31
|
+
- **Release claim audit** — `docs/V3_2_AUDIT.md` records a strict 20-claim
|
|
32
|
+
PASS/PARTIAL/FAIL matrix with implementation evidence, fixes, validation,
|
|
33
|
+
artifact readiness, and release metadata policy.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Version bumped to 3.2.0 across Python, npm, the VS Code extension, the v3
|
|
38
|
+
asset manifest, and runtime version constants.
|
|
39
|
+
- `/app#/agents` now exposes the Agent Registry API directly, including
|
|
40
|
+
registry metadata, capability discovery, enablement, and custom-agent
|
|
41
|
+
registration.
|
|
42
|
+
- `/app#/skills` now normalizes the live `/workspace/skills` response shape
|
|
43
|
+
(`installed`, `available`, and object/array `registry`) instead of only a
|
|
44
|
+
legacy `skills` array.
|
|
45
|
+
- MCP/skills/plugin-directory routes are mounted once through the tools router;
|
|
46
|
+
route compatibility tests now guard against duplicate public path/method
|
|
47
|
+
registrations.
|
|
48
|
+
|
|
49
|
+
### Notes
|
|
50
|
+
|
|
51
|
+
- Validation covered lint, typecheck, Python compile, 365 unit tests, live
|
|
52
|
+
integration tests, 90 Playwright tests, real `/app` browser route validation,
|
|
53
|
+
Python/npm/VSIX builds, and exact-version release artifact validation.
|
|
54
|
+
- No packages were published and nothing was deployed.
|
|
55
|
+
|
|
56
|
+
## [3.1.0] - 2026-06-07
|
|
57
|
+
|
|
58
|
+
> v3.1 — Mainline Product Platform Completion. `/app` is now the full
|
|
59
|
+
> non-enterprise local-first workspace: Classic pages are compatibility routes,
|
|
60
|
+
> production embedding profiles are explicit, AgentRuntime and registries are
|
|
61
|
+
> the integration boundaries, and v3 runtime assets are hash-manifested.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **Hashed asset pipeline** — `npm run build:assets` writes
|
|
66
|
+
`static/v3/asset-manifest.json`, hashed CSS/JS siblings, and import-rewritten
|
|
67
|
+
ES modules. `/app` reads the manifest and loads hashed assets automatically.
|
|
68
|
+
- **Production embedding profiles** — local `bge-m3`, `nomic-embed-text`,
|
|
69
|
+
`e5-large`, `gte-large`; Ollama `nomic-embed-text`, `mxbai-embed-large`,
|
|
70
|
+
BGE-M3-compatible providers; MLX Apple Silicon profiles; and
|
|
71
|
+
OpenAI-compatible `text-embedding-3-small` / `text-embedding-3-large`.
|
|
72
|
+
- **Native model lifecycle controls** — `/app#/models` now calls the real
|
|
73
|
+
`/models/load` and `/models/unload/{model_id}` endpoints.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
|
|
77
|
+
- **Classic retirement** — normal user workflows no longer link to Classic
|
|
78
|
+
Chat, Classic Runtime, or Classic Admin. Compatibility routes remain available
|
|
79
|
+
for rollback/debug.
|
|
80
|
+
- **Truthful unavailable states** — v3 fallback adapters return empty
|
|
81
|
+
unavailable payloads instead of sample data, fake counters, or fabricated
|
|
82
|
+
health.
|
|
83
|
+
- **Release metadata** — package, npm, VS Code extension, Workspace OS, docs,
|
|
84
|
+
and expected artifacts are aligned at `3.1.0`.
|
|
85
|
+
|
|
86
|
+
### Validation
|
|
87
|
+
|
|
88
|
+
- Release target: `npm run lint`, `npm run typecheck`, `npm run check:python`,
|
|
89
|
+
backend/integration tests, Playwright visual tests, `python -m build`,
|
|
90
|
+
`npm run build`, `npm pack`, VSIX package, and exact-version artifact
|
|
91
|
+
validation.
|
|
92
|
+
|
|
3
93
|
## [3.0.0] - 2026-06-07
|
|
4
94
|
|
|
5
95
|
> v3 — Local-first AI Workspace Platform. The hybrid-search
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Lattice AI v3.2.0 Release Claim Audit
|
|
2
|
+
|
|
3
|
+
Audit date: 2026-06-08
|
|
4
|
+
Target version: 3.2.0
|
|
5
|
+
Scope: code, API routes, frontend views, adapters, tests, runtime/browser behavior, artifacts, tag/release metadata.
|
|
6
|
+
|
|
7
|
+
## Verdict
|
|
8
|
+
|
|
9
|
+
Final source verdict: PASS after release-hardening fixes.
|
|
10
|
+
|
|
11
|
+
The audit found two UI wiring gaps and one release-route hygiene issue:
|
|
12
|
+
|
|
13
|
+
- `/app#/agents` showed AgentRuntime state but did not expose the Agent Registry controls and capability index from `/agents/api/registry*`.
|
|
14
|
+
- `/app#/skills` called the live skill registry but only normalized a legacy `{ skills: [...] }` shape, so the real `{ installed, available, registry }` payload rendered as empty.
|
|
15
|
+
- `server_app` registered the MCP router twice because `create_tools_router` already mounted the MCP/skills/plugin-directory routes.
|
|
16
|
+
|
|
17
|
+
All three were fixed and covered by tests. No product scope was added.
|
|
18
|
+
|
|
19
|
+
## PASS / PARTIAL / FAIL Matrix
|
|
20
|
+
|
|
21
|
+
| # | Claim | Status | Evidence |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| 1 | Multi-Agent Collaboration | PASS | `latticeai/core/multi_agent.py`, `latticeai/services/agent_runtime.py`, `/agents/api/run`, handoffs/context packets/replay tests in `tests/unit/test_multi_agent.py` and `tests/unit/test_agent_platform_maturity.py`. |
|
|
24
|
+
| 2 | Agent Registry | PASS | `latticeai/core/agent_registry.py`, `latticeai/api/agent_registry.py`, `/agents/api/registry*`, `/app#/agents` registry section, visual coverage in `tests/visual/v3.spec.js`. |
|
|
25
|
+
| 3 | Agent Marketplace | PASS | `latticeai/core/marketplace.py`, `latticeai/api/marketplace.py`, `/marketplace/templates`, `/app#/marketplace`. Offline local catalog; no cloud marketplace is claimed. |
|
|
26
|
+
| 4 | Agent Templates | PASS | Five named agent templates: Research Assistant, Coding Assistant, Knowledge Curator, Documentation Writer, Workflow Builder; clone/export/import/install covered in `tests/unit/test_v32_platform.py`. |
|
|
27
|
+
| 5 | Workflow Agents | PASS | `latticeai/core/workflow_engine.py`, `latticeai/api/workflow_designer.py`, `PlatformRuntime.build_workflow_runners`, `/app#/workflows`, workflow run/replay tests. |
|
|
28
|
+
| 6 | Autonomous Planning | PASS | Planner/executor/reviewer flow via `/agents/api/run`, bounded retry/replan history in `MultiAgentOrchestrator`, `/app#/planning`, retry tests. |
|
|
29
|
+
| 7 | Long-Term Memory Platform | PASS | `latticeai/services/memory_service.py` unifies workspace/project/agent/conversation/graph/vector tiers; `/api/memory/*`. |
|
|
30
|
+
| 8 | Memory Manager | PASS | Usage/sources/health plus recall/inspect/prune/compact/rebuild/clear in `MemoryService` and `/app#/memory`. |
|
|
31
|
+
| 9 | Skills Registry | PASS | `WorkspaceOSStore.list_skill_registry`, `/workspace/skills*`, `/app#/skills`; fixed live payload normalization and added visual coverage. |
|
|
32
|
+
| 10 | Hooks Registry | PASS | `latticeai/core/hooks.py`, `latticeai/api/hooks.py`, `/api/hooks*`, `/app#/hooks`, unit tests for toggle/register/inspect/remove. |
|
|
33
|
+
| 11 | Tool Registry | PASS | `latticeai/core/tool_registry.py`, `latticeai/services/tool_dispatch.py`, `/tools/permissions`, `/mcp/tools`, `/app#/tools`, route duplicate guard. |
|
|
34
|
+
| 12 | MCP Foundation / MCP Manager | PASS | `latticeai/api/mcp.py`, `/mcp/tools`, `/mcp/installed`, `/mcp/custom`, `/mcp/claude-code-servers`, `/app#/mcp`; duplicate mount removed. |
|
|
35
|
+
| 13 | Retrieval & Memory Integration | PASS | `latticeai/api/search.py`, `latticeai/services/search_service.py`, `MemoryService.recall`, `/api/search/hybrid`, `/api/index/status`, `/api/graph`, `/api/memory/recall`. |
|
|
36
|
+
| 14 | UX Completion Pass | PASS | `/app` routes render without Classic dependency, console errors, app 404/500 responses, or horizontal overflow in real browser validation. Light/dark toggle validated. |
|
|
37
|
+
| 15 | Documentation refresh | PASS | README, release notes/changelogs, release guide, and this audit document updated for the hardening pass. |
|
|
38
|
+
| 16 | Architecture refresh | PASS | Architecture docs already describe the v3.2 runtime/registry boundaries; code now matches those boundaries without duplicate MCP route registration. |
|
|
39
|
+
| 17 | Version 3.2.0 consistency | PASS | `package.json`, `pyproject.toml`, `vscode-extension/package.json`, runtime constants, and expected artifacts remain at 3.2.0. |
|
|
40
|
+
| 18 | Tests | PASS | Lint/typecheck/python compile, 365 unit tests, live integration tests, 90 Playwright tests, and real-app browser route validation passed. |
|
|
41
|
+
| 19 | Build artifacts | PASS | Rebuilt Python sdist/wheel, npm tarball, VSIX, and validated exact v3.2.0 artifact names. |
|
|
42
|
+
| 20 | Git tag and GitHub Release metadata | PASS | Source policy keeps version 3.2.0; tag/release metadata should point at the final validated main commit. No package assets were attached or published. |
|
|
43
|
+
|
|
44
|
+
## Fixes Applied
|
|
45
|
+
|
|
46
|
+
### Agent Registry UI wiring
|
|
47
|
+
|
|
48
|
+
- Finding: Agent Registry backend and adapter existed, but `/app#/agents` only rendered the runtime roster.
|
|
49
|
+
- Affected files: `static/v3/js/views/agents.js`, hashed v3 assets, `tests/visual/v3.spec.js`, `tests/visual/mock_server.cjs`.
|
|
50
|
+
- Expected behavior: `/app#/agents` exposes built-in/custom registry metadata, versions, capabilities, enabled state, and registration.
|
|
51
|
+
- Actual behavior before fix: Registry data was reachable only through API/adapters, not the product view.
|
|
52
|
+
- Fix: Added a live Agent Registry section, custom-agent registration controls, capability index display, enable/disable/remove actions, and Playwright coverage.
|
|
53
|
+
|
|
54
|
+
### Skills registry live payload normalization
|
|
55
|
+
|
|
56
|
+
- Finding: `/workspace/skills` returned `{ installed, available, registry }`, but `/app#/skills` only accepted a legacy `{ skills }` array.
|
|
57
|
+
- Affected files: `static/v3/js/views/skills.js`, hashed v3 assets, `tests/visual/v3.spec.js`, `tests/visual/mock_server.cjs`.
|
|
58
|
+
- Expected behavior: installed and available skills render from the real registry payload.
|
|
59
|
+
- Actual behavior before fix: live registry payload could render as an empty state.
|
|
60
|
+
- Fix: Normalized installed, available, array registry, and object registry shapes with de-duplication by skill name.
|
|
61
|
+
|
|
62
|
+
### Duplicate MCP route registration
|
|
63
|
+
|
|
64
|
+
- Finding: `create_tools_router` already included the MCP router, and `server_app` mounted it again.
|
|
65
|
+
- Affected files: `latticeai/server_app.py`, `tests/unit/test_route_compatibility.py`.
|
|
66
|
+
- Expected behavior: every public path/method has one handler.
|
|
67
|
+
- Actual behavior before fix: duplicate MCP/skills/plugin-directory path registrations.
|
|
68
|
+
- Fix: Removed the second MCP include and added a route duplicate guard.
|
|
69
|
+
|
|
70
|
+
## Validation Summary
|
|
71
|
+
|
|
72
|
+
- `npm run lint`: PASS
|
|
73
|
+
- `npm run typecheck`: PASS
|
|
74
|
+
- `npm run check:python`: PASS
|
|
75
|
+
- `./venv/bin/python -m pytest tests/unit -v`: PASS, 365 passed
|
|
76
|
+
- `LTCAI_TEST_BASE_URL=http://127.0.0.1:8899 ./venv/bin/python -m pytest tests/integration -v`: PASS, 9 passed
|
|
77
|
+
- `npx playwright test`: PASS, 90 passed
|
|
78
|
+
- Real app browser validation: PASS for `/app`, `/app#/chat`, `/app#/planning`, `/app#/workflows`, `/app#/marketplace`, `/app#/memory`, `/app#/skills`, `/app#/hooks`, `/app#/tools`, `/app#/mcp`, `/app#/agents`, `/app#/models`, `/app#/knowledge-graph`, `/app#/hybrid-search`, `/app#/settings` across desktop/mobile, including no console/page errors, no app 404/500 responses, no horizontal overflow, no Classic dependency text, and light/dark toggle.
|
|
79
|
+
|
|
80
|
+
## Release Readiness
|
|
81
|
+
|
|
82
|
+
READY FOR MANUAL PACKAGE PUBLICATION once the v3.2.0 tag and GitHub Release notes point at the final validated main commit. No package publication or deployment was performed by this audit.
|
package/docs/V3_FRONTEND.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> A token-native single-page workspace shell for Lattice AI v3. It ships as the
|
|
4
4
|
> primary product experience and calls the real v3 retrieval/chat APIs while
|
|
5
|
-
>
|
|
5
|
+
> rendering clear unavailable states for local services that are not running.
|
|
6
6
|
|
|
7
7
|
Entry point: **`/app`** (served by `latticeai/api/static_routes.py` →
|
|
8
8
|
`static/v3/index.html`). Login, auto-login after registration, SSO callback, and
|
|
9
9
|
the PWA manifest land on `/app`. The legacy multi-page screens (`/workspace`,
|
|
10
|
-
`/chat`, `/graph`, `/admin`, …) remain reachable
|
|
11
|
-
|
|
10
|
+
`/chat`, `/graph`, `/admin`, …) remain reachable only as compatibility/debug
|
|
11
|
+
routes; normal user workflows stay in `/app`.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -49,6 +49,9 @@ Administration Users · Permissions · Audit Logs · Security · Policies · Pri
|
|
|
49
49
|
Token-native, layered on the existing **single color source**
|
|
50
50
|
(`static/css/tokens.css`, `data-lt-theme` light/dark). No legacy override layers
|
|
51
51
|
(`responsive.css` / `workspace.css` / `platform.css`) are loaded by v3.
|
|
52
|
+
Runtime assets are loaded through `static/v3/asset-manifest.json`; the build
|
|
53
|
+
step writes hashed CSS/JS siblings and `/app` reads the manifest instead of
|
|
54
|
+
using manual `?v=` query strings.
|
|
52
55
|
|
|
53
56
|
| File | Responsibility |
|
|
54
57
|
| --- | --- |
|
|
@@ -91,28 +94,27 @@ export async function render(ctx) { /* … */ return singleDomNode; }
|
|
|
91
94
|
## Integration contract
|
|
92
95
|
|
|
93
96
|
`core/api.js` is the only transport layer. Every call hits the **real** endpoint
|
|
94
|
-
first and
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
placeholder responses, so nothing fake is presented as backend output.
|
|
97
|
+
first and returns `{ ok, status, data, source }` where `source` is `"live"` or
|
|
98
|
+
`"unavailable"`. Unavailable responses carry empty data so primary surfaces do
|
|
99
|
+
not invent counters, sample graphs, fake health, or fake run history.
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
Core live surfaces established before v3.2.0:
|
|
100
102
|
|
|
101
|
-
| Adapter | Endpoint |
|
|
103
|
+
| Adapter | Endpoint | Unavailable state |
|
|
102
104
|
| --- | --- | --- |
|
|
103
|
-
| `api.indexStatus()` | `GET /api/index/status` |
|
|
104
|
-
| `api.graph(params)` | `GET /api/graph` → `GET /knowledge-graph/graph` |
|
|
105
|
-
| `api.hybridSearch(q, opts)` | `POST /api/search/hybrid` |
|
|
106
|
-
| `api.streamChat(body)` | `POST /chat` SSE |
|
|
105
|
+
| `api.indexStatus()` | `GET /api/index/status` | empty retrieval status |
|
|
106
|
+
| `api.graph(params)` | `GET /api/graph` → `GET /knowledge-graph/graph` | empty graph |
|
|
107
|
+
| `api.hybridSearch(q, opts)` | `POST /api/search/hybrid` | empty results |
|
|
108
|
+
| `api.streamChat(body)` | `POST /chat` SSE | unavailable chat message |
|
|
107
109
|
|
|
108
110
|
No backend retrieval logic is implemented in the frontend — only transport,
|
|
109
111
|
normalization, clear provenance badges, and graceful fallback. When the live
|
|
110
112
|
chat backend reports `no_model_loaded`, v3 Chat shows a setup message instead of
|
|
111
|
-
falling back to
|
|
113
|
+
falling back to generated text.
|
|
112
114
|
|
|
113
|
-
Embedding disclosure:
|
|
114
|
-
`lattice-local-hash-v1
|
|
115
|
-
|
|
115
|
+
Embedding disclosure: production profiles are exposed for local/Ollama/MLX and
|
|
116
|
+
OpenAI-compatible providers. `lattice-local-hash-v1` remains a deterministic
|
|
117
|
+
fallback and is not presented as a production semantic embedding model.
|
|
116
118
|
|
|
117
119
|
---
|
|
118
120
|
|
|
@@ -131,6 +133,7 @@ focus rings, skip link, keyboard command palette (⌘K / Ctrl-K).
|
|
|
131
133
|
## Validation
|
|
132
134
|
|
|
133
135
|
- `npm run lint` (extended to cover `static/v3/**` via `scripts/lint_v3.mjs`).
|
|
136
|
+
- `npm run build:assets` (writes hashed v3 assets and `asset-manifest.json`).
|
|
134
137
|
- `npm run test:visual` (`tests/visual/v3.spec.js` against the mock server, which
|
|
135
138
|
serves `/app` and mocks the future API surfaces).
|
|
136
139
|
- Browser-rendered smoke checks of every route in light and dark themes.
|
package/docs/architecture.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Lattice AI Architecture
|
|
2
2
|
|
|
3
|
+
> v3.2.0 — feature-complete for non-enterprise use cases. The agent ecosystem
|
|
4
|
+
> (registry, marketplace + templates, workflow agents, autonomous planning),
|
|
5
|
+
> the long-term memory platform + manager, and the skills/hooks/tool/MCP
|
|
6
|
+
> registries are all operable from `/app`. Enterprise controls remain future
|
|
7
|
+
> work.
|
|
8
|
+
|
|
3
9
|
Lattice AI is a local-first **AI workspace, AI pipeline platform, Knowledge
|
|
4
10
|
Graph platform, and multi-agent workflow platform**. The architecture is
|
|
5
11
|
organized around one durable center: the Knowledge Graph. Models, tools,
|
package/latticeai/__init__.py
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Agent Registry API router (v3.2.0).
|
|
2
|
+
|
|
3
|
+
Exposes :class:`~latticeai.core.agent_registry.AgentRegistry` so registration,
|
|
4
|
+
discovery, metadata, versioning, capabilities, and configuration are reachable
|
|
5
|
+
from the /app Agents view. Paths sit under ``/agents/api/registry`` alongside
|
|
6
|
+
the existing runtime endpoints. Full paths in decorators (no ``prefix=``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
from latticeai.core.agent_registry import AgentRegistry
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentRegisterRequest(BaseModel):
|
|
20
|
+
name: str
|
|
21
|
+
type: str = "custom"
|
|
22
|
+
description: str = ""
|
|
23
|
+
capabilities: List[str] = []
|
|
24
|
+
config: Dict[str, Any] = {}
|
|
25
|
+
version: str = "1.0.0"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AgentConfigRequest(BaseModel):
|
|
29
|
+
config: Dict[str, Any] = {}
|
|
30
|
+
enabled: Optional[bool] = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_agent_registry_router(
|
|
34
|
+
*,
|
|
35
|
+
registry: AgentRegistry,
|
|
36
|
+
require_user: Callable[[Request], str],
|
|
37
|
+
append_audit_event: Callable[..., None],
|
|
38
|
+
) -> APIRouter:
|
|
39
|
+
router = APIRouter()
|
|
40
|
+
|
|
41
|
+
@router.get("/agents/api/registry")
|
|
42
|
+
async def list_registry(request: Request, type: Optional[str] = None):
|
|
43
|
+
require_user(request)
|
|
44
|
+
return registry.list(agent_type=type)
|
|
45
|
+
|
|
46
|
+
@router.get("/agents/api/registry/capabilities")
|
|
47
|
+
async def registry_capabilities(request: Request):
|
|
48
|
+
require_user(request)
|
|
49
|
+
return {"capabilities": registry.capabilities()}
|
|
50
|
+
|
|
51
|
+
@router.get("/agents/api/registry/discover")
|
|
52
|
+
async def registry_discover(request: Request, capability: str = ""):
|
|
53
|
+
require_user(request)
|
|
54
|
+
return {"capability": capability, "agents": registry.discover(capability)}
|
|
55
|
+
|
|
56
|
+
@router.post("/agents/api/registry")
|
|
57
|
+
async def register_agent(req: AgentRegisterRequest, request: Request):
|
|
58
|
+
user = require_user(request)
|
|
59
|
+
try:
|
|
60
|
+
entry = registry.register(
|
|
61
|
+
name=req.name,
|
|
62
|
+
agent_type=req.type,
|
|
63
|
+
description=req.description,
|
|
64
|
+
capabilities=req.capabilities,
|
|
65
|
+
config=req.config,
|
|
66
|
+
version=req.version,
|
|
67
|
+
)
|
|
68
|
+
except ValueError as exc:
|
|
69
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
70
|
+
append_audit_event("agent_register", user_email=user, agent_id=entry["id"], type=entry["type"])
|
|
71
|
+
return {"agent": entry}
|
|
72
|
+
|
|
73
|
+
@router.get("/agents/api/registry/{agent_id:path}")
|
|
74
|
+
async def get_agent(agent_id: str, request: Request):
|
|
75
|
+
require_user(request)
|
|
76
|
+
agent = registry.get(agent_id)
|
|
77
|
+
if agent is None:
|
|
78
|
+
raise HTTPException(status_code=404, detail=f"Agent not found: {agent_id}")
|
|
79
|
+
return {"agent": agent}
|
|
80
|
+
|
|
81
|
+
@router.patch("/agents/api/registry/{agent_id:path}")
|
|
82
|
+
async def update_agent(agent_id: str, req: AgentConfigRequest, request: Request):
|
|
83
|
+
user = require_user(request)
|
|
84
|
+
try:
|
|
85
|
+
agent = registry.update_config(agent_id, req.config, enabled=req.enabled)
|
|
86
|
+
except KeyError as exc:
|
|
87
|
+
raise HTTPException(status_code=404, detail=f"Agent not found: {agent_id}") from exc
|
|
88
|
+
append_audit_event("agent_config", user_email=user, agent_id=agent_id)
|
|
89
|
+
return {"agent": agent}
|
|
90
|
+
|
|
91
|
+
@router.delete("/agents/api/registry/{agent_id:path}")
|
|
92
|
+
async def remove_agent(agent_id: str, request: Request):
|
|
93
|
+
user = require_user(request)
|
|
94
|
+
try:
|
|
95
|
+
result = registry.remove(agent_id)
|
|
96
|
+
except KeyError as exc:
|
|
97
|
+
raise HTTPException(status_code=404, detail=f"Agent not found: {agent_id}") from exc
|
|
98
|
+
except ValueError as exc:
|
|
99
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
100
|
+
append_audit_event("agent_remove", user_email=user, agent_id=agent_id)
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
return router
|
package/latticeai/api/auth.py
CHANGED
|
@@ -57,6 +57,7 @@ def create_auth_router(
|
|
|
57
57
|
public_sso_config: Callable[..., Dict],
|
|
58
58
|
open_registration: bool,
|
|
59
59
|
session_ttl: int,
|
|
60
|
+
require_auth: bool = True,
|
|
60
61
|
) -> APIRouter:
|
|
61
62
|
router = APIRouter()
|
|
62
63
|
|
|
@@ -221,7 +222,9 @@ def create_auth_router(
|
|
|
221
222
|
async def get_profile(request: Request):
|
|
222
223
|
email = require_user(request)
|
|
223
224
|
if not email:
|
|
224
|
-
|
|
225
|
+
if require_auth:
|
|
226
|
+
raise HTTPException(status_code=401, detail="인증이 필요합니다.")
|
|
227
|
+
return {"email": "", "name": "Local User", "nickname": "You", "role": "admin", "is_admin": True}
|
|
225
228
|
users = load_users()
|
|
226
229
|
user = users.get(email)
|
|
227
230
|
if not user:
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Hooks platform API router (v3.2.0).
|
|
2
|
+
|
|
3
|
+
Exposes the lifecycle :class:`~latticeai.core.hooks.HooksRegistry` over HTTP so
|
|
4
|
+
the /app Hooks view can list, inspect, enable/disable, reorder, and register
|
|
5
|
+
hooks. Full paths live in the decorators (no ``prefix=``), matching the rest of
|
|
6
|
+
the API.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Callable, List, Optional
|
|
12
|
+
|
|
13
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
from latticeai.core.hooks import HooksRegistry
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HookToggleRequest(BaseModel):
|
|
20
|
+
hook_id: str
|
|
21
|
+
enabled: bool = True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HookReorderRequest(BaseModel):
|
|
25
|
+
kind: str
|
|
26
|
+
ordered_ids: List[str] = []
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HookRegisterRequest(BaseModel):
|
|
30
|
+
name: str
|
|
31
|
+
kind: str
|
|
32
|
+
description: str = ""
|
|
33
|
+
command: str = ""
|
|
34
|
+
order: Optional[int] = None
|
|
35
|
+
enabled: bool = True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_hooks_router(
|
|
39
|
+
*,
|
|
40
|
+
registry: HooksRegistry,
|
|
41
|
+
require_user: Callable[[Request], str],
|
|
42
|
+
append_audit_event: Callable[..., None],
|
|
43
|
+
) -> APIRouter:
|
|
44
|
+
router = APIRouter()
|
|
45
|
+
|
|
46
|
+
@router.get("/api/hooks")
|
|
47
|
+
async def list_hooks(request: Request, kind: Optional[str] = None):
|
|
48
|
+
require_user(request)
|
|
49
|
+
return registry.list(kind=kind)
|
|
50
|
+
|
|
51
|
+
@router.get("/api/hooks/{hook_id:path}")
|
|
52
|
+
async def inspect_hook(hook_id: str, request: Request):
|
|
53
|
+
require_user(request)
|
|
54
|
+
try:
|
|
55
|
+
return {"hook": registry.inspect(hook_id)}
|
|
56
|
+
except KeyError as exc:
|
|
57
|
+
raise HTTPException(status_code=404, detail=f"Hook not found: {hook_id}") from exc
|
|
58
|
+
|
|
59
|
+
@router.post("/api/hooks/enable")
|
|
60
|
+
async def enable_hook(req: HookToggleRequest, request: Request):
|
|
61
|
+
user = require_user(request)
|
|
62
|
+
try:
|
|
63
|
+
hook = registry.set_enabled(req.hook_id, req.enabled)
|
|
64
|
+
except KeyError as exc:
|
|
65
|
+
raise HTTPException(status_code=404, detail=f"Hook not found: {req.hook_id}") from exc
|
|
66
|
+
append_audit_event("hook_toggle", user_email=user, hook_id=req.hook_id, enabled=req.enabled)
|
|
67
|
+
return {"hook": hook}
|
|
68
|
+
|
|
69
|
+
@router.post("/api/hooks/disable")
|
|
70
|
+
async def disable_hook(req: HookToggleRequest, request: Request):
|
|
71
|
+
user = require_user(request)
|
|
72
|
+
try:
|
|
73
|
+
hook = registry.set_enabled(req.hook_id, False)
|
|
74
|
+
except KeyError as exc:
|
|
75
|
+
raise HTTPException(status_code=404, detail=f"Hook not found: {req.hook_id}") from exc
|
|
76
|
+
append_audit_event("hook_toggle", user_email=user, hook_id=req.hook_id, enabled=False)
|
|
77
|
+
return {"hook": hook}
|
|
78
|
+
|
|
79
|
+
@router.post("/api/hooks/reorder")
|
|
80
|
+
async def reorder_hooks(req: HookReorderRequest, request: Request):
|
|
81
|
+
require_user(request)
|
|
82
|
+
return registry.reorder(req.kind, req.ordered_ids)
|
|
83
|
+
|
|
84
|
+
@router.post("/api/hooks/register")
|
|
85
|
+
async def register_hook(req: HookRegisterRequest, request: Request):
|
|
86
|
+
user = require_user(request)
|
|
87
|
+
try:
|
|
88
|
+
entry = registry.register(
|
|
89
|
+
name=req.name,
|
|
90
|
+
kind=req.kind,
|
|
91
|
+
description=req.description,
|
|
92
|
+
command=req.command,
|
|
93
|
+
order=req.order,
|
|
94
|
+
enabled=req.enabled,
|
|
95
|
+
)
|
|
96
|
+
except ValueError as exc:
|
|
97
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
98
|
+
append_audit_event("hook_register", user_email=user, hook_id=entry["id"], kind=entry["kind"])
|
|
99
|
+
return {"hook": entry}
|
|
100
|
+
|
|
101
|
+
@router.delete("/api/hooks/{hook_id:path}")
|
|
102
|
+
async def remove_hook(hook_id: str, request: Request):
|
|
103
|
+
user = require_user(request)
|
|
104
|
+
try:
|
|
105
|
+
result = registry.remove(hook_id)
|
|
106
|
+
except KeyError as exc:
|
|
107
|
+
raise HTTPException(status_code=404, detail=f"Hook not found: {hook_id}") from exc
|
|
108
|
+
except ValueError as exc:
|
|
109
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
110
|
+
append_audit_event("hook_remove", user_email=user, hook_id=hook_id)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
return router
|