ltcai 7.8.0 → 8.0.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 +39 -31
- package/docs/CHANGELOG.md +38 -0
- package/docs/DEVELOPMENT.md +1 -2
- package/docs/MULTI_AGENT_RUNTIME.md +6 -5
- package/docs/V2_ARCHITECTURE.md +3 -2
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/projection.py +19 -5
- package/lattice_brain/graph/provenance.py +26 -4
- package/lattice_brain/graph/write_master.py +30 -3
- package/lattice_brain/runtime/__init__.py +8 -2
- package/lattice_brain/runtime/agent_runtime.py +17 -1
- package/lattice_brain/runtime/contracts.py +64 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/core/agent.py +39 -12
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/tool_registry.py +7 -0
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/architecture_readiness.py +49 -6
- package/latticeai/services/product_readiness.py +26 -17
- package/latticeai/services/tool_dispatch.py +15 -3
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lattice AI
|
|
2
2
|
|
|
3
|
-
**Lattice AI
|
|
3
|
+
**Lattice AI 8.0 is the local-first Digital Brain platform with hardened runtime architecture. It keeps your knowledge durable across any AI model, with AgentRuntime, ToolRegistry, central Config, and Knowledge Graph stability tracked by explicit release contracts.**
|
|
4
4
|
|
|
5
5
|
**Lattice AI는 모델이 바뀌어도 내 지식과 맥락을 보존하는 로컬 우선 AI 브레인입니다.**
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ downloads, update checks, and other external communication happen only after
|
|
|
11
11
|
explicit consent.
|
|
12
12
|
|
|
13
13
|
It is not a ChatGPT clone, a model launcher, a graph database, or a note app.
|
|
14
|
-
It is the finished private AI memory layer wrapped in a Living Brain experience — now with
|
|
14
|
+
It is the finished private AI memory layer wrapped in a Living Brain experience — now with the 8.0 runtime architecture contract behind it.
|
|
15
15
|
|
|
16
16
|
[](https://pypi.org/project/ltcai/)
|
|
17
17
|
[](https://www.npmjs.com/package/ltcai)
|
|
@@ -64,7 +64,7 @@ You need Lattice AI when:
|
|
|
64
64
|
|
|
65
65
|
## Living Brain Flow
|
|
66
66
|
|
|
67
|
-
The screenshots below are release evidence captures. The
|
|
67
|
+
The screenshots below are release evidence captures. The 8.0 line keeps the
|
|
68
68
|
same first-run flow and rebuilds the Brain Chat Home layout so chat and the
|
|
69
69
|
composer are the first visible action.
|
|
70
70
|
|
|
@@ -78,28 +78,28 @@ confirm owner, check the computer, choose the Brain voice.
|
|
|
78
78
|
Choose the owner of the Brain. The profile is not a SaaS account by default; it
|
|
79
79
|
is the local identity for the knowledge you keep.
|
|
80
80
|
|
|
81
|
-

|
|
82
82
|
|
|
83
83
|
### 3. Environment Analysis
|
|
84
84
|
|
|
85
85
|
See what kind of local AI experience this computer can support before choosing a
|
|
86
86
|
model.
|
|
87
87
|
|
|
88
|
-

|
|
89
89
|
|
|
90
90
|
### 4. Recommended Models
|
|
91
91
|
|
|
92
92
|
Start with a short list: safest recommendation, faster model, stronger model.
|
|
93
93
|
Advanced details stay available without overwhelming first-time users.
|
|
94
94
|
|
|
95
|
-

|
|
96
96
|
|
|
97
97
|
### 5. Install And Load
|
|
98
98
|
|
|
99
99
|
Download and load only after consent. Lattice explains model size, local
|
|
100
100
|
execution, and network use before work starts.
|
|
101
101
|
|
|
102
|
-

|
|
103
103
|
|
|
104
104
|
### 6. Brain Chat
|
|
105
105
|
|
|
@@ -107,14 +107,14 @@ Talk normally. Useful decisions and context become memory, then appear later as
|
|
|
107
107
|
topics, relationships, graph structure, and the concentric memory rings around
|
|
108
108
|
the Brain.
|
|
109
109
|
|
|
110
|
-

|
|
111
111
|
|
|
112
112
|
### 7. Review Center
|
|
113
113
|
|
|
114
114
|
Automation results are staged for review before they become durable decisions.
|
|
115
115
|
Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
|
|
116
116
|
|
|
117
|
-

|
|
118
118
|
|
|
119
119
|
## Brain Depths
|
|
120
120
|
|
|
@@ -130,10 +130,10 @@ The user travels inward from everyday memory to deeper structure:
|
|
|
130
130
|
|
|
131
131
|
Walkthrough:
|
|
132
132
|
|
|
133
|
-

|
|
134
134
|
|
|
135
135
|
Screenshot index and capture notes:
|
|
136
|
-
[output/release/v7.
|
|
136
|
+
[output/release/v7.8.0/SCREENSHOT_INDEX.md](output/release/v7.8.0/SCREENSHOT_INDEX.md)
|
|
137
137
|
|
|
138
138
|
## Install
|
|
139
139
|
|
|
@@ -210,33 +210,39 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
|
|
|
210
210
|
|
|
211
211
|
## Current Release
|
|
212
212
|
|
|
213
|
-
The current release is **
|
|
214
|
-
|
|
215
|
-
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
the
|
|
221
|
-
-
|
|
222
|
-
|
|
213
|
+
The current release is **8.0.0 — Runtime Architecture Contract**:
|
|
214
|
+
|
|
215
|
+
- `architecture_readiness()` now emits `lattice-architecture-contract/v1`,
|
|
216
|
+
including the preferred refactoring order and concrete ownership for
|
|
217
|
+
AgentRuntime, ToolRegistry, central Config, server decomposition, and KG
|
|
218
|
+
hardening.
|
|
219
|
+
- The product AgentRuntime facade in `lattice_brain.runtime.agent_runtime`
|
|
220
|
+
remains the unambiguous `/agents` execution and observability boundary.
|
|
221
|
+
- ToolRegistry manifest output now carries `tool-registry-contract/v1` with
|
|
222
|
+
dispatch, policy, and permission ownership, so runtime/admin surfaces can
|
|
223
|
+
detect catalog drift from one source of truth.
|
|
224
|
+
- Knowledge Graph hardening is represented as an additive reprojection strategy
|
|
225
|
+
with legacy read compatibility and non-destructive rollback/export paths.
|
|
226
|
+
- KG v2 read-equivalence is locked for document listing, node lookup,
|
|
227
|
+
relationship search, and traversal, while logical imports preserve colliding
|
|
228
|
+
legacy edge labels without breaking native canonical dedupe.
|
|
223
229
|
- Product readiness is machine-checkable through `product_readiness()`, which
|
|
224
230
|
composes the architecture contract with exact artifact/docs/workflow evidence.
|
|
225
|
-
- CI and release workflows
|
|
226
|
-
|
|
231
|
+
- CI and release workflows continue to run frontend lint/typecheck/build gates
|
|
232
|
+
plus the 8.0 product readiness score before a tag can be treated as release-safe.
|
|
227
233
|
|
|
228
|
-
Expected artifacts for
|
|
234
|
+
Expected artifacts for 8.0.0 release must use exact filenames:
|
|
229
235
|
|
|
230
|
-
- `dist/ltcai-
|
|
231
|
-
- `dist/ltcai-
|
|
232
|
-
- `ltcai-
|
|
233
|
-
- `dist/ltcai-
|
|
234
|
-
- `src-tauri/target/release/bundle/dmg/Lattice
|
|
236
|
+
- `dist/ltcai-8.0.0-py3-none-any.whl`
|
|
237
|
+
- `dist/ltcai-8.0.0.tar.gz`
|
|
238
|
+
- `ltcai-8.0.0.tgz`
|
|
239
|
+
- `dist/ltcai-8.0.0.vsix`
|
|
240
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_8.0.0_aarch64.dmg`
|
|
235
241
|
|
|
236
|
-
Do not
|
|
242
|
+
Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
|
|
237
243
|
|
|
238
244
|
See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
|
|
239
|
-
strategic roadmap slices applied through
|
|
245
|
+
strategic roadmap slices applied through 8.0.0 and the follow-up tracks.
|
|
240
246
|
|
|
241
247
|
## Known Limitations
|
|
242
248
|
|
|
@@ -253,6 +259,8 @@ strategic roadmap slices applied through 7.8.0 and the follow-up tracks.
|
|
|
253
259
|
|
|
254
260
|
| Version | Theme |
|
|
255
261
|
| --- | --- |
|
|
262
|
+
| 8.0.0 | Runtime Architecture Contract: AgentRuntime, ToolRegistry, central Config, server decomposition, and KG hardening are captured as machine-checkable release boundaries with exact 8.0.0 artifacts |
|
|
263
|
+
| 7.9.0 | Agent Runtime Boundary Hardening: explicit `SingleAgentRuntime`, compatibility alias preservation, injected rollback port, and release/readiness docs aligned to the product AgentRuntime facade |
|
|
256
264
|
| 7.8.0 | Brain Chat Home UX Simplification: chat-first first viewport, visible workspace navigation, collapsed source/status utilities, hidden default depth controls, and removal of obsolete Brain UX components |
|
|
257
265
|
| 7.7.0 | Complete Product Polish: command-center Brain Home, repaired Review Center evidence, exact 7.7 docs/artifacts, product readiness gate, and stronger CI/release checks |
|
|
258
266
|
| 7.6.0 | Brain-Centered UX & Architecture Closure: Wake Brain first-run surface, concentric memory rings with direct depth controls, and machine-checkable closure of the two architecture/UX review files |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@ existed at that release.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [8.0.0] - 2026-06-24
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Added `lattice-architecture-contract/v1` to make the AgentRuntime,
|
|
13
|
+
ToolRegistry, Config, server decomposition, and Knowledge Graph stabilization
|
|
14
|
+
boundaries explicit and testable for the major architecture line.
|
|
15
|
+
- Added `tool-registry-contract/v1` to the ToolRegistry manifest, including
|
|
16
|
+
dispatch, policy, and permission ownership.
|
|
17
|
+
- Updated architecture and product readiness targets to 8.0.0.
|
|
18
|
+
- Synchronized package/runtime/static/Tauri metadata to 8.0.0.
|
|
19
|
+
- Updated current-release docs and exact artifact names to 8.0.0 while
|
|
20
|
+
preserving historical 7.x entries.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Made logical Knowledge Graph `replace` imports transactional so malformed
|
|
24
|
+
imports roll back without clearing the existing graph.
|
|
25
|
+
- Locked v2 read-equivalence coverage for `list_documents`, `get_node`,
|
|
26
|
+
`relationship_search`, and `traverse`.
|
|
27
|
+
- Preserved colliding legacy edge labels during logical import/backfill while
|
|
28
|
+
keeping native write-door synonym dedupe canonical.
|
|
29
|
+
|
|
30
|
+
## [7.9.0] - 2026-06-23
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Added `SingleAgentRuntime` as the explicit name for the legacy single-agent
|
|
34
|
+
state machine while preserving `AgentRuntime` as a compatibility alias.
|
|
35
|
+
- Updated tool dispatch to build `SingleAgentRuntime` directly.
|
|
36
|
+
- Moved single-agent git rollback behind an injected `rollback_file` port owned
|
|
37
|
+
by `ToolDispatchService`, keeping shell execution out of the core state
|
|
38
|
+
machine.
|
|
39
|
+
- Added a shared `runtime-boundary/v1` descriptor so product and single-agent
|
|
40
|
+
runtime boundaries are machine-readable in config/tests.
|
|
41
|
+
- Added `RuntimeBoundaryProtocol` as the minimal shared inspection surface for
|
|
42
|
+
runtime-boundary-aware dependency injection.
|
|
43
|
+
- Updated architecture and product readiness targets to 7.9.0.
|
|
44
|
+
|
|
9
45
|
## [7.8.0] - 2026-06-22
|
|
10
46
|
|
|
11
47
|
### Changed
|
|
@@ -23,6 +59,8 @@ existed at that release.
|
|
|
23
59
|
- Moved draft onboarding polish out of inline styles and into the shared design
|
|
24
60
|
stylesheet with responsive behavior and bilingual copy.
|
|
25
61
|
- Updated architecture and product readiness targets to 7.8.0.
|
|
62
|
+
- Refreshed 7.8.0 release screenshots, walkthrough video/GIF, and capture
|
|
63
|
+
notes under `output/release/v7.8.0/`.
|
|
26
64
|
|
|
27
65
|
## [7.7.0] - 2026-06-22
|
|
28
66
|
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -95,5 +95,4 @@ For user-facing, API, runtime, release, or packaging changes, check:
|
|
|
95
95
|
- `docs/LEGACY_COMPATIBILITY.md` when root compatibility files change
|
|
96
96
|
|
|
97
97
|
Release/publish examples must use exact target-version filenames. Do not
|
|
98
|
-
document wildcard upload commands
|
|
99
|
-
|
|
98
|
+
document wildcard artifact upload commands.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Multi-Agent Runtime is the **orchestration layer** introduced in v2.0.0 and
|
|
4
4
|
operationalized in v2.2.0. It sits
|
|
5
|
-
*above* the v1.x single-agent state machine ([`
|
|
5
|
+
*above* the v1.x single-agent state machine ([`SingleAgentRuntime`](../latticeai/core/agent.py))
|
|
6
6
|
and coordinates a pipeline of named **roles** that hand off work to one another,
|
|
7
7
|
retry on a failing review, and emit a fully observable, replayable timeline.
|
|
8
8
|
|
|
@@ -34,20 +34,21 @@ objects durable and inspectable:
|
|
|
34
34
|
|
|
35
35
|
## How it relates to the v1 single-agent runtime
|
|
36
36
|
|
|
37
|
-
v1.x shipped a single-agent state machine — `
|
|
37
|
+
v1.x shipped a single-agent state machine — `SingleAgentRuntime` driving
|
|
38
38
|
`PLAN → EXECUTE → VERIFY → DONE` (with `ROLLBACK` / `FAILED` recovery paths) over an
|
|
39
|
-
injected `AgentDeps` port.
|
|
39
|
+
injected `AgentDeps` port. The historical `AgentRuntime` import remains as a
|
|
40
|
+
compatibility alias.
|
|
40
41
|
|
|
41
42
|
v2.0 adds the *orchestration* layer on top: instead of one agent looping through
|
|
42
43
|
internal phases, the `MultiAgentOrchestrator` drives a **pipeline of distinct roles**
|
|
43
44
|
(researcher, planner, executor, reviewer, release) that hand off to one another and
|
|
44
45
|
can rewind on a failing review. The two layers are complementary — the v2
|
|
45
46
|
orchestrator coordinates roles; an individual role's runner could itself be backed by
|
|
46
|
-
the v1 `
|
|
47
|
+
the v1 `SingleAgentRuntime` loop or an LLM, but that is an implementation choice behind the
|
|
47
48
|
injected runner port.
|
|
48
49
|
|
|
49
50
|
> **Compatibility.** The Multi-Agent Runtime is purely additive. The v1
|
|
50
|
-
> `
|
|
51
|
+
> `SingleAgentRuntime` state machine and its `/agent` endpoints are untouched, and the v2
|
|
51
52
|
> API is namespaced under `/agents` (plural) so it never collides with the existing
|
|
52
53
|
> single-agent `/agent` routes. Existing v1.x data is preserved; new runs are appended
|
|
53
54
|
> to workspace state and the Knowledge Graph alongside it.
|
package/docs/V2_ARCHITECTURE.md
CHANGED
|
@@ -141,8 +141,9 @@ portable JSON representation (definition only — no run history or scope).
|
|
|
141
141
|
|
|
142
142
|
### 1.3 Multi-Agent Runtime 2.0 (`latticeai.core.multi_agent`)
|
|
143
143
|
|
|
144
|
-
v1.x shipped a single-agent state machine (`latticeai.core.agent.
|
|
145
|
-
|
|
144
|
+
v1.x shipped a single-agent state machine (`latticeai.core.agent.SingleAgentRuntime`;
|
|
145
|
+
`latticeai.core.agent.AgentRuntime` remains a compatibility alias) that drives
|
|
146
|
+
PLAN → EXECUTE → VERIFY → DONE. v2.0 adds the **orchestration** layer above it:
|
|
146
147
|
a pipeline of named roles that hand off to one another, retry on a failing
|
|
147
148
|
review, and emit a structured timeline that drops straight into the Workspace
|
|
148
149
|
timeline / Knowledge Graph.
|
|
@@ -331,17 +331,31 @@ class KnowledgeGraphProjectionMixin:
|
|
|
331
331
|
edge_id: Optional[str] = None,
|
|
332
332
|
created_at: Optional[str] = None,
|
|
333
333
|
strict: bool = False,
|
|
334
|
+
legacy_type: Optional[str] = None,
|
|
334
335
|
) -> None:
|
|
335
336
|
if KGStoreV2 is None:
|
|
336
337
|
if strict:
|
|
337
338
|
raise RuntimeError("Knowledge Graph v2 schema is unavailable")
|
|
338
339
|
return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
)
|
|
340
|
+
explicit_legacy_type = legacy_type is not None
|
|
341
|
+
leg_type = legacy_type if explicit_legacy_type else edge_type
|
|
342
|
+
# Native canonical writes (and write-door dedupes) use legacy_type=''
|
|
343
|
+
# so (source,target,type) is the effective key.
|
|
344
|
+
# Import paths can pass distinct legacy_type to keep colliding legacy
|
|
345
|
+
# labels as separate rows (lossless for old data).
|
|
346
|
+
if leg_type and EdgeType is not None:
|
|
347
|
+
try:
|
|
348
|
+
if EdgeType.from_legacy(leg_type).value == leg_type:
|
|
349
|
+
leg_type = ""
|
|
350
|
+
except ValueError:
|
|
351
|
+
pass
|
|
342
352
|
norm_type = (
|
|
343
353
|
EdgeType.from_legacy(edge_type).value if EdgeType is not None else edge_type
|
|
344
354
|
)
|
|
355
|
+
if explicit_legacy_type and leg_type:
|
|
356
|
+
eid = f"edge:{_sha256_text(f'{from_node}|{norm_type}|{to_node}|{leg_type}')[:24]}"
|
|
357
|
+
else:
|
|
358
|
+
eid = edge_id or f"edge:{_sha256_text(f'{from_node}|{norm_type}|{to_node}')[:24]}"
|
|
345
359
|
meta_str = metadata_json if metadata_json is not None else "{}"
|
|
346
360
|
confidence = float(_safe_loads(meta_str).get("confidence", 1.0))
|
|
347
361
|
try:
|
|
@@ -360,7 +374,7 @@ class KnowledgeGraphProjectionMixin:
|
|
|
360
374
|
from_node,
|
|
361
375
|
to_node,
|
|
362
376
|
norm_type,
|
|
363
|
-
|
|
377
|
+
leg_type,
|
|
364
378
|
float(weight),
|
|
365
379
|
confidence,
|
|
366
380
|
meta_str,
|
|
@@ -371,7 +385,7 @@ class KnowledgeGraphProjectionMixin:
|
|
|
371
385
|
# (the UNIQUE upsert + weight=max alone would erase recurrence).
|
|
372
386
|
row = conn.execute(
|
|
373
387
|
"SELECT id FROM edges_v2 WHERE source=? AND target=? AND type=? AND legacy_type=?",
|
|
374
|
-
(from_node, to_node, norm_type,
|
|
388
|
+
(from_node, to_node, norm_type, leg_type),
|
|
375
389
|
).fetchone()
|
|
376
390
|
if row is not None:
|
|
377
391
|
conn.execute(
|
|
@@ -292,10 +292,24 @@ class KnowledgeGraphProvenanceMixin:
|
|
|
292
292
|
plan["dry_run"] = True
|
|
293
293
|
return plan
|
|
294
294
|
|
|
295
|
-
if mode == "replace":
|
|
296
|
-
self.clear_all()
|
|
297
|
-
|
|
298
295
|
with self._connect() as conn:
|
|
296
|
+
if mode == "replace":
|
|
297
|
+
# Keep replacement imports transactional. The old clear_all()
|
|
298
|
+
# path committed before the import started, so a malformed
|
|
299
|
+
# artifact could leave a cleared or partially rebuilt graph.
|
|
300
|
+
# These deletes roll back with the rest of the import.
|
|
301
|
+
for table in (
|
|
302
|
+
"local_file_index",
|
|
303
|
+
"knowledge_sources",
|
|
304
|
+
"chunks",
|
|
305
|
+
"edges",
|
|
306
|
+
"nodes",
|
|
307
|
+
"vector_embeddings",
|
|
308
|
+
):
|
|
309
|
+
conn.execute(f"DELETE FROM {table}")
|
|
310
|
+
if KGStoreV2 is not None:
|
|
311
|
+
conn.execute("DELETE FROM edges_v2")
|
|
312
|
+
conn.execute("DELETE FROM nodes_v2")
|
|
299
313
|
for n in nodes:
|
|
300
314
|
self._upsert_node(
|
|
301
315
|
conn,
|
|
@@ -315,13 +329,21 @@ class KnowledgeGraphProvenanceMixin:
|
|
|
315
329
|
metadata=_safe_loads(c.get("metadata_json")),
|
|
316
330
|
)
|
|
317
331
|
for e in edges:
|
|
332
|
+
e_meta = _safe_loads(e.get("metadata_json")) or {}
|
|
333
|
+
leg_label = e_meta.get("legacy_label")
|
|
334
|
+
if not leg_label:
|
|
335
|
+
orig = e.get("type") or ""
|
|
336
|
+
if orig:
|
|
337
|
+
# preserve whatever label came from export (legacy or canon)
|
|
338
|
+
leg_label = orig
|
|
318
339
|
self._upsert_edge(
|
|
319
340
|
conn,
|
|
320
341
|
e["from_node"],
|
|
321
342
|
e["to_node"],
|
|
322
343
|
e["type"],
|
|
323
344
|
weight=float(e.get("weight") or 1.0),
|
|
324
|
-
metadata=
|
|
345
|
+
metadata=e_meta,
|
|
346
|
+
legacy_label=leg_label,
|
|
325
347
|
)
|
|
326
348
|
for s in sources:
|
|
327
349
|
conn.execute(
|
|
@@ -73,20 +73,46 @@ class KnowledgeGraphWriteMixin:
|
|
|
73
73
|
edge_type: str,
|
|
74
74
|
weight: float = 1.0,
|
|
75
75
|
metadata: Optional[Dict[str, Any]] = None,
|
|
76
|
+
*,
|
|
77
|
+
legacy_label: Optional[str] = None,
|
|
76
78
|
) -> str:
|
|
77
79
|
# v4 write door: every new edge stores the canonical EdgeType value —
|
|
78
80
|
# free-string types (e.g. '포함함', '언급함') are normalized here, so no
|
|
79
81
|
# caller can mint new legacy taxonomy. The original label survives in
|
|
80
82
|
# metadata.legacy_label for traceability.
|
|
83
|
+
#
|
|
84
|
+
# legacy_type in edges_v2:
|
|
85
|
+
# - normal write door: synonyms always dedupe to ONE row with legacy_type=''
|
|
86
|
+
# - import (passes legacy_label=) : preserves distinct legacy labels as
|
|
87
|
+
# separate v2 rows (lossless collision for old artifacts / backfill)
|
|
88
|
+
passed_for_legacy = legacy_label or edge_type
|
|
81
89
|
if EdgeType is not None:
|
|
82
90
|
canonical = EdgeType.from_legacy(edge_type).value
|
|
83
|
-
if canonical != edge_type:
|
|
91
|
+
if canonical != edge_type or (legacy_label and legacy_label != canonical):
|
|
84
92
|
metadata = dict(metadata or {})
|
|
85
|
-
|
|
93
|
+
ll = legacy_label or edge_type
|
|
94
|
+
if ll != canonical:
|
|
95
|
+
metadata.setdefault("legacy_label", ll)
|
|
86
96
|
edge_type = canonical
|
|
87
97
|
edge_id = f"edge:{_sha256_text(f'{from_node}|{edge_type}|{to_node}')[:24]}"
|
|
88
98
|
now = _now()
|
|
89
99
|
meta_json = _json(metadata) # canonical string shared with the projection
|
|
100
|
+
v2_legacy = ""
|
|
101
|
+
if passed_for_legacy and passed_for_legacy != edge_type:
|
|
102
|
+
if legacy_label is not None:
|
|
103
|
+
# explicit legacy_label from import path forces distinct legacy_type
|
|
104
|
+
# rows in v2 for collision preservation
|
|
105
|
+
v2_legacy = passed_for_legacy
|
|
106
|
+
else:
|
|
107
|
+
# normal write-door dedupes even for synonym labels (legacy_type='')
|
|
108
|
+
v2_legacy = ""
|
|
109
|
+
# v2 may use distinct eid when preserving legacy collisions (different from
|
|
110
|
+
# the canon-based edge_id used for legacy edges table which always dedupes)
|
|
111
|
+
v2_eid = (
|
|
112
|
+
f"edge:{_sha256_text(f'{from_node}|{edge_type}|{to_node}|{v2_legacy}')[:24]}"
|
|
113
|
+
if v2_legacy
|
|
114
|
+
else edge_id
|
|
115
|
+
)
|
|
90
116
|
self._v2_project_edge(
|
|
91
117
|
conn,
|
|
92
118
|
from_node,
|
|
@@ -94,9 +120,10 @@ class KnowledgeGraphWriteMixin:
|
|
|
94
120
|
edge_type,
|
|
95
121
|
float(weight),
|
|
96
122
|
meta_json,
|
|
97
|
-
edge_id=
|
|
123
|
+
edge_id=v2_eid,
|
|
98
124
|
created_at=now,
|
|
99
125
|
strict=True,
|
|
126
|
+
legacy_type=v2_legacy,
|
|
100
127
|
)
|
|
101
128
|
conn.execute(
|
|
102
129
|
"""
|
|
@@ -44,9 +44,9 @@ orchestrator, and the agent runtime service. Lazy-loaded so importing
|
|
|
44
44
|
/agents/api/runtime/* , POST /agents (start via runtime.start or executor)
|
|
45
45
|
api/chat.py
|
|
46
46
|
from latticeai.services.tool_dispatch import build_agent_runtime
|
|
47
|
-
-> latticeai/core/agent.py:
|
|
47
|
+
-> latticeai/core/agent.py:SingleAgentRuntime (별도 state/plan/transcript 머신, single-agent /agent 경로. dispatch_tool 만 공유)
|
|
48
48
|
latticeai/services/tool_dispatch.py:14
|
|
49
|
-
from latticeai.core.agent import
|
|
49
|
+
from latticeai.core.agent import SingleAgentRuntime
|
|
50
50
|
(tool governance + core single-agent용)
|
|
51
51
|
latticeai/services/platform_runtime.py
|
|
52
52
|
from lattice_brain.runtime.{hooks, multi_agent}
|
|
@@ -54,6 +54,7 @@ orchestrator, and the agent runtime service. Lazy-loaded so importing
|
|
|
54
54
|
|
|
55
55
|
core/tool_registry.py (신규) + services/tool_dispatch.py 가 tool build 주도.
|
|
56
56
|
lattice_brain/runtime 는 multi-agent + hooks + facade 에 집중. core/agent 는 chat/agent 단일 루프.
|
|
57
|
+
기존 latticeai.core.agent.AgentRuntime import 는 SingleAgentRuntime 호환 alias 로 유지.
|
|
57
58
|
|
|
58
59
|
이 매핑으로 중복 제거 및 wiring 명확화 완료 (feat(Act, automation) 방향).
|
|
59
60
|
"""
|
|
@@ -64,6 +65,7 @@ __all__ = [
|
|
|
64
65
|
"AgentRuntime",
|
|
65
66
|
"AgentRuntimeUnavailable",
|
|
66
67
|
"MultiAgentOrchestrator",
|
|
68
|
+
"RuntimeBoundaryProtocol",
|
|
67
69
|
"HooksRegistry",
|
|
68
70
|
"dispatch_tool",
|
|
69
71
|
]
|
|
@@ -78,6 +80,10 @@ def __getattr__(name: str):
|
|
|
78
80
|
from .multi_agent import MultiAgentOrchestrator
|
|
79
81
|
|
|
80
82
|
return MultiAgentOrchestrator
|
|
83
|
+
if name == "RuntimeBoundaryProtocol":
|
|
84
|
+
from .contracts import RuntimeBoundaryProtocol
|
|
85
|
+
|
|
86
|
+
return RuntimeBoundaryProtocol
|
|
81
87
|
if name in {"HooksRegistry", "dispatch_tool"}:
|
|
82
88
|
from . import hooks
|
|
83
89
|
|
|
@@ -37,7 +37,13 @@ from .multi_agent import (
|
|
|
37
37
|
MULTI_AGENT_VERSION,
|
|
38
38
|
ROLE_AGENT_IDS,
|
|
39
39
|
)
|
|
40
|
-
from .contracts import
|
|
40
|
+
from .contracts import (
|
|
41
|
+
contract_view,
|
|
42
|
+
contract_views,
|
|
43
|
+
extract_contract,
|
|
44
|
+
multi_agent_contract,
|
|
45
|
+
runtime_boundary_contract,
|
|
46
|
+
)
|
|
41
47
|
|
|
42
48
|
ROLE_DESCRIPTIONS = {
|
|
43
49
|
"researcher": "Gathers workspace context and memory for the goal.",
|
|
@@ -91,10 +97,20 @@ class AgentRuntime:
|
|
|
91
97
|
def _execution_mode(self) -> str:
|
|
92
98
|
return "async" if self._run_executor is not None else "synchronous"
|
|
93
99
|
|
|
100
|
+
def boundary(self) -> Dict[str, Any]:
|
|
101
|
+
return runtime_boundary_contract(
|
|
102
|
+
name="AgentRuntime",
|
|
103
|
+
runtime="multi_agent",
|
|
104
|
+
entrypoint="lattice_brain.runtime.agent_runtime.AgentRuntime",
|
|
105
|
+
surface="/agents",
|
|
106
|
+
owns="product agent execution, observability, status, health, events, replay, and stop",
|
|
107
|
+
)
|
|
108
|
+
|
|
94
109
|
# ── configuration ─────────────────────────────────────────────────────
|
|
95
110
|
def config(self) -> Dict[str, Any]:
|
|
96
111
|
return {
|
|
97
112
|
"version": MULTI_AGENT_VERSION,
|
|
113
|
+
"boundary": self.boundary(),
|
|
98
114
|
"roles": list(AGENT_ROLES),
|
|
99
115
|
"default_pipeline": list(CORE_PIPELINE),
|
|
100
116
|
"max_retries_cap": self._max_retries_cap,
|
|
@@ -30,7 +30,7 @@ from __future__ import annotations
|
|
|
30
30
|
|
|
31
31
|
from dataclasses import dataclass, field
|
|
32
32
|
from datetime import datetime
|
|
33
|
-
from typing import Any, Dict, Iterable, List, Optional
|
|
33
|
+
from typing import Any, Dict, Iterable, List, Optional, Protocol, runtime_checkable
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def runtime_timestamp() -> str:
|
|
@@ -69,6 +69,8 @@ CONTRACT_VIEW_KEYS = (
|
|
|
69
69
|
"blocking_reasons",
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
+
RUNTIME_BOUNDARY_SCHEMA = "runtime-boundary/v1"
|
|
73
|
+
|
|
72
74
|
_SCHEMA_FOR_KIND = {
|
|
73
75
|
"agent_run": AGENT_RUN_SCHEMA,
|
|
74
76
|
"workflow_run": WORKFLOW_RUN_SCHEMA,
|
|
@@ -223,6 +225,67 @@ def multi_agent_contract(
|
|
|
223
225
|
).as_dict()
|
|
224
226
|
|
|
225
227
|
|
|
228
|
+
@dataclass(frozen=True)
|
|
229
|
+
class RuntimeBoundaryContract:
|
|
230
|
+
"""Machine-readable descriptor for an execution runtime boundary.
|
|
231
|
+
|
|
232
|
+
This is intentionally separate from run records: it lets tests, routers, and
|
|
233
|
+
docs assert which class owns which execution surface without importing the
|
|
234
|
+
wrong runtime by name.
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
name: str
|
|
238
|
+
runtime: str
|
|
239
|
+
entrypoint: str
|
|
240
|
+
surface: str
|
|
241
|
+
owns: str
|
|
242
|
+
compatibility_aliases: List[str] = field(default_factory=list)
|
|
243
|
+
|
|
244
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
245
|
+
return {
|
|
246
|
+
"schema_version": RUNTIME_BOUNDARY_SCHEMA,
|
|
247
|
+
"name": self.name,
|
|
248
|
+
"runtime": self.runtime,
|
|
249
|
+
"entrypoint": self.entrypoint,
|
|
250
|
+
"surface": self.surface,
|
|
251
|
+
"owns": self.owns,
|
|
252
|
+
"compatibility_aliases": list(self.compatibility_aliases),
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def runtime_boundary_contract(
|
|
257
|
+
*,
|
|
258
|
+
name: str,
|
|
259
|
+
runtime: str,
|
|
260
|
+
entrypoint: str,
|
|
261
|
+
surface: str,
|
|
262
|
+
owns: str,
|
|
263
|
+
compatibility_aliases: Optional[List[str]] = None,
|
|
264
|
+
) -> Dict[str, Any]:
|
|
265
|
+
return RuntimeBoundaryContract(
|
|
266
|
+
name=name,
|
|
267
|
+
runtime=runtime,
|
|
268
|
+
entrypoint=entrypoint,
|
|
269
|
+
surface=surface,
|
|
270
|
+
owns=owns,
|
|
271
|
+
compatibility_aliases=list(compatibility_aliases or []),
|
|
272
|
+
).as_dict()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@runtime_checkable
|
|
276
|
+
class RuntimeBoundaryProtocol(Protocol):
|
|
277
|
+
"""Minimal shared surface for runtime boundary discovery.
|
|
278
|
+
|
|
279
|
+
Product ``AgentRuntime`` and core ``SingleAgentRuntime`` intentionally keep
|
|
280
|
+
different execution methods. This protocol only fixes the common inspection
|
|
281
|
+
surface that DI, readiness gates, and tests can depend on safely.
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
def boundary(self) -> Dict[str, Any]: ...
|
|
285
|
+
|
|
286
|
+
def config(self) -> Dict[str, Any]: ...
|
|
287
|
+
|
|
288
|
+
|
|
226
289
|
def run_record_contract(run: Dict[str, Any], *, runtime: str = "multi_agent") -> Dict[str, Any]:
|
|
227
290
|
"""Build the family contract from a persisted agent run row."""
|
|
228
291
|
run = dict(run or {})
|
|
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
21
21
|
from .contracts import multi_agent_contract
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
MULTI_AGENT_VERSION = "
|
|
24
|
+
MULTI_AGENT_VERSION = "8.0.0"
|
|
25
25
|
|
|
26
26
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
27
27
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
package/latticeai/core/agent.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Single-agent runtime — the Discover→Plan→Implement→Verify state machine.
|
|
2
2
|
|
|
3
|
-
This module is the deep
|
|
4
|
-
``
|
|
3
|
+
This module is the deep single-agent loop: a small interface (``AgentDeps`` ports +
|
|
4
|
+
``SingleAgentRuntime.run_to_completion``) over the whole role-phased state machine
|
|
5
5
|
(planner → executor → critic → rollback → memory). It carries no FastAPI,
|
|
6
6
|
no globals, and no I/O of its own — every collaborator is injected through
|
|
7
7
|
``AgentDeps``.
|
|
@@ -23,14 +23,13 @@ from __future__ import annotations
|
|
|
23
23
|
import json
|
|
24
24
|
import logging
|
|
25
25
|
import re
|
|
26
|
-
import subprocess
|
|
27
26
|
from dataclasses import dataclass
|
|
28
27
|
from enum import Enum
|
|
29
28
|
from pathlib import Path
|
|
30
29
|
from typing import Any, Awaitable, Callable, Dict, FrozenSet, List, Optional
|
|
31
30
|
|
|
32
31
|
from lattice_brain.runtime.hooks import dispatch_tool
|
|
33
|
-
from lattice_brain.runtime.contracts import single_agent_contract
|
|
32
|
+
from lattice_brain.runtime.contracts import runtime_boundary_contract, single_agent_contract
|
|
34
33
|
from tools import ToolError
|
|
35
34
|
|
|
36
35
|
|
|
@@ -92,7 +91,7 @@ def extract_action(raw: str) -> Dict:
|
|
|
92
91
|
|
|
93
92
|
@dataclass
|
|
94
93
|
class AgentDeps:
|
|
95
|
-
"""The ports
|
|
94
|
+
"""The ports a :class:`SingleAgentRuntime` needs from the outside world.
|
|
96
95
|
|
|
97
96
|
Everything the state machine touches is here, so the loop can be exercised
|
|
98
97
|
against fakes. See module docstring for the two-adapter rationale.
|
|
@@ -125,6 +124,11 @@ class AgentDeps:
|
|
|
125
124
|
memory_updater_prompt: str
|
|
126
125
|
agent_root: Path
|
|
127
126
|
|
|
127
|
+
# ── rollback port (optional) ─────────────────────────────────────
|
|
128
|
+
# Production injects this from the tool dispatch service so this pure
|
|
129
|
+
# state machine does not shell out directly. Tests can pass a recorder.
|
|
130
|
+
rollback_file: Optional[Callable[[str], Dict[str, Any]]] = None
|
|
131
|
+
|
|
128
132
|
# ── lifecycle hooks port (optional) ──────────────────────────────
|
|
129
133
|
# When present, every tool execution fires the shared pre_tool/post_tool
|
|
130
134
|
# lifecycle, so the agent tool path no longer bypasses hooks.
|
|
@@ -137,12 +141,30 @@ class AgentDeps:
|
|
|
137
141
|
brain_memory: Any = None
|
|
138
142
|
|
|
139
143
|
|
|
140
|
-
class
|
|
144
|
+
class SingleAgentRuntime:
|
|
141
145
|
"""Drives the agent state machine over injected :class:`AgentDeps`."""
|
|
142
146
|
|
|
143
147
|
def __init__(self, deps: AgentDeps) -> None:
|
|
144
148
|
self.deps = deps
|
|
145
149
|
|
|
150
|
+
def boundary(self) -> Dict[str, Any]:
|
|
151
|
+
return runtime_boundary_contract(
|
|
152
|
+
name="SingleAgentRuntime",
|
|
153
|
+
runtime="single_agent",
|
|
154
|
+
entrypoint="latticeai.core.agent.SingleAgentRuntime",
|
|
155
|
+
surface="/agent",
|
|
156
|
+
owns="single-agent PLAN / EXECUTE / VERIFY state machine over injected ports",
|
|
157
|
+
compatibility_aliases=["latticeai.core.agent.AgentRuntime"],
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def config(self) -> Dict[str, Any]:
|
|
161
|
+
return {
|
|
162
|
+
"boundary": self.boundary(),
|
|
163
|
+
"states": [state.value for state in AgentState],
|
|
164
|
+
"terminal_states": sorted(state.value for state in AGENT_TERMINAL_STATES),
|
|
165
|
+
"execution_mode": "injected_ports",
|
|
166
|
+
}
|
|
167
|
+
|
|
146
168
|
def contract(self, ctx: AgentRunContext, req: Any, *, run_id: Optional[str] = None) -> Dict[str, Any]:
|
|
147
169
|
"""Expose the shared agent-run contract for the single-agent loop."""
|
|
148
170
|
return single_agent_contract(ctx=ctx, goal=getattr(req, "message", ""), run_id=run_id)
|
|
@@ -405,12 +427,11 @@ class AgentRuntime:
|
|
|
405
427
|
path = result.get("path") or (step.get("args") or {}).get("path", "")
|
|
406
428
|
if not path:
|
|
407
429
|
continue
|
|
430
|
+
if d.rollback_file is None:
|
|
431
|
+
rolled.append({"path": path, "ok": False, "error": "rollback_file port is not configured"})
|
|
432
|
+
continue
|
|
408
433
|
try:
|
|
409
|
-
|
|
410
|
-
["git", "checkout", "--", path], cwd=str(d.agent_root),
|
|
411
|
-
capture_output=True, text=True, timeout=10,
|
|
412
|
-
)
|
|
413
|
-
rolled.append({"path": path, "ok": r.returncode == 0, "stderr": r.stderr[:200]})
|
|
434
|
+
rolled.append(d.rollback_file(str(path)))
|
|
414
435
|
except Exception as exc:
|
|
415
436
|
rolled.append({"path": path, "ok": False, "error": str(exc)})
|
|
416
437
|
|
|
@@ -491,3 +512,9 @@ class AgentRuntime:
|
|
|
491
512
|
ctx.state = AgentState.FAILED
|
|
492
513
|
|
|
493
514
|
ctx.state_history.append(ctx.state.value)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# Backward compatibility: external callers historically imported
|
|
518
|
+
# ``latticeai.core.agent.AgentRuntime`` for the single-agent state machine.
|
|
519
|
+
# The product/runtime facade lives at ``lattice_brain.runtime.agent_runtime``.
|
|
520
|
+
AgentRuntime = SingleAgentRuntime
|
|
@@ -292,7 +292,14 @@ class ToolRegistry:
|
|
|
292
292
|
})
|
|
293
293
|
diagnostics = self.diagnostics()
|
|
294
294
|
return {
|
|
295
|
+
"schema_version": "tool-registry-contract/v1",
|
|
295
296
|
"status": "ok" if diagnostics["ready"] else "degraded",
|
|
297
|
+
"boundary": {
|
|
298
|
+
"owner": "latticeai.core.tool_registry.ToolRegistry",
|
|
299
|
+
"dispatch_owner": "tools.DEFAULT_TOOL_REGISTRY",
|
|
300
|
+
"policy_owner": "latticeai.core.tool_registry.ToolRegistry",
|
|
301
|
+
"permission_owner": "latticeai.services.tool_dispatch.ToolDispatchService",
|
|
302
|
+
},
|
|
296
303
|
"catalog_brief": self.catalog_brief.strip(),
|
|
297
304
|
"diagnostics": diagnostics,
|
|
298
305
|
"tools": tools,
|
|
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
|
21
21
|
from lattice_brain.runtime.contracts import realtime_event_contract, run_record_contract, workflow_run_contract
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
WORKSPACE_OS_VERSION = "
|
|
24
|
+
WORKSPACE_OS_VERSION = "8.0.0"
|
|
25
25
|
|
|
26
26
|
# Workspace types separate single-user Personal workspaces from shared
|
|
27
27
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""Machine-checkable architecture readiness gates for release work.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
and testable before the release can be called complete.
|
|
3
|
+
8.0.0 turns the major architecture priorities into an explicit release
|
|
4
|
+
contract. AgentRuntime, ToolRegistry, central Config, decomposed server runtime,
|
|
5
|
+
and Knowledge Graph stabilization must be discoverable, ordered, and backed by
|
|
6
|
+
tests before the release can be called complete.
|
|
8
7
|
"""
|
|
9
8
|
|
|
10
9
|
from __future__ import annotations
|
|
@@ -15,6 +14,19 @@ from pathlib import Path
|
|
|
15
14
|
from typing import Any, Dict, List
|
|
16
15
|
|
|
17
16
|
|
|
17
|
+
ARCHITECTURE_VERSION_TARGET = "8.0.0"
|
|
18
|
+
|
|
19
|
+
PREFERRED_REFACTORING_ORDER = [
|
|
20
|
+
"agent-runtime",
|
|
21
|
+
"tool-registry",
|
|
22
|
+
"config-centralization",
|
|
23
|
+
"server-decomposition",
|
|
24
|
+
"kg-hardening",
|
|
25
|
+
"documentation-sync",
|
|
26
|
+
"ui-enhancements",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
18
30
|
@dataclass(frozen=True)
|
|
19
31
|
class ArchitectureGate:
|
|
20
32
|
id: str
|
|
@@ -108,12 +120,43 @@ def architecture_readiness(root: Path | None = None) -> Dict[str, Any]:
|
|
|
108
120
|
|
|
109
121
|
api_router_count = len(list((root / "latticeai" / "api").glob("*.py")))
|
|
110
122
|
runtime_module_count = len(list((root / "latticeai" / "runtime").glob("*.py")))
|
|
123
|
+
ordered_gate_ids = [gate.id for gate in gates]
|
|
124
|
+
contract = {
|
|
125
|
+
"schema_version": "lattice-architecture-contract/v1",
|
|
126
|
+
"version_target": ARCHITECTURE_VERSION_TARGET,
|
|
127
|
+
"refactoring_order": list(PREFERRED_REFACTORING_ORDER),
|
|
128
|
+
"boundaries": {
|
|
129
|
+
"agent-runtime": {
|
|
130
|
+
"owner": "lattice_brain.runtime.agent_runtime.AgentRuntime",
|
|
131
|
+
"surface": "/agents",
|
|
132
|
+
"status": "facade",
|
|
133
|
+
},
|
|
134
|
+
"tool-registry": {
|
|
135
|
+
"owner": "latticeai.core.tool_registry.ToolRegistry",
|
|
136
|
+
"surface": "/tools",
|
|
137
|
+
"status": "registry",
|
|
138
|
+
},
|
|
139
|
+
"config-centralization": {
|
|
140
|
+
"owner": "latticeai.core.config.Config",
|
|
141
|
+
"surface": "composition root",
|
|
142
|
+
"status": "typed-config",
|
|
143
|
+
},
|
|
144
|
+
"kg-hardening": {
|
|
145
|
+
"owner": "lattice_brain.graph.store.KnowledgeGraphStore",
|
|
146
|
+
"strategy": "additive reprojection with legacy read compatibility",
|
|
147
|
+
"rollback": "portable export/import and non-destructive migration paths",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
"ordered_gate_ids": ordered_gate_ids,
|
|
151
|
+
}
|
|
111
152
|
return {
|
|
112
153
|
"status": "complete" if all(gate.status == "complete" for gate in gates) else "incomplete",
|
|
113
|
-
"version_target":
|
|
154
|
+
"version_target": ARCHITECTURE_VERSION_TARGET,
|
|
155
|
+
"contract": contract,
|
|
114
156
|
"gates": [gate.__dict__ for gate in gates],
|
|
115
157
|
"metrics": {
|
|
116
158
|
"api_router_modules": api_router_count,
|
|
117
159
|
"runtime_modules": runtime_module_count,
|
|
160
|
+
"architecture_gates": len(gates),
|
|
118
161
|
},
|
|
119
162
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
"""Machine-checkable *product* readiness gates for the
|
|
1
|
+
"""Machine-checkable *product* readiness gates for the 8.0 line.
|
|
2
2
|
|
|
3
3
|
Where ``architecture_readiness`` proves the internal structure is sound, this
|
|
4
|
-
module answers the product question the
|
|
5
|
-
|
|
6
|
-
gate is backed by evidence that is probed on disk, so a gate
|
|
7
|
-
``complete`` when its evidence actually resolves. The same report
|
|
8
|
-
by ``scripts/product_readiness.py`` and re-run after every
|
|
9
|
-
point: completeness is something we keep measuring, not a
|
|
4
|
+
module answers the product question the 8.0 release exists to settle: *are the
|
|
5
|
+
runtime boundaries still clear and release-ready after hardening?* It does so
|
|
6
|
+
honestly — every gate is backed by evidence that is probed on disk, so a gate
|
|
7
|
+
only reports ``complete`` when its evidence actually resolves. The same report
|
|
8
|
+
can be printed by ``scripts/product_readiness.py`` and re-run after every
|
|
9
|
+
change, which is the point: completeness is something we keep measuring, not a
|
|
10
|
+
one-time claim.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from __future__ import annotations
|
|
@@ -17,7 +18,7 @@ from typing import Any, Dict, List
|
|
|
17
18
|
|
|
18
19
|
from latticeai.services.architecture_readiness import architecture_readiness
|
|
19
20
|
|
|
20
|
-
PRODUCT_VERSION_TARGET = "
|
|
21
|
+
PRODUCT_VERSION_TARGET = "8.0.0"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@dataclass(frozen=True)
|
|
@@ -64,10 +65,10 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
64
65
|
evidence=[
|
|
65
66
|
"package.json::release:artifacts",
|
|
66
67
|
"package.json::release:validate",
|
|
67
|
-
"README.md::dist/ltcai-
|
|
68
|
-
"README.md::dist/ltcai-
|
|
69
|
-
"README.md::dist/ltcai-
|
|
70
|
-
"README.md::ltcai-
|
|
68
|
+
"README.md::dist/ltcai-8.0.0-py3-none-any.whl",
|
|
69
|
+
"README.md::dist/ltcai-8.0.0.tar.gz",
|
|
70
|
+
"README.md::dist/ltcai-8.0.0.vsix",
|
|
71
|
+
"README.md::ltcai-8.0.0.tgz",
|
|
71
72
|
"scripts/validate_release_artifacts.py",
|
|
72
73
|
"scripts/release_smoke.py",
|
|
73
74
|
"Dockerfile",
|
|
@@ -83,12 +84,20 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
83
84
|
title="Release story is documented and honest",
|
|
84
85
|
evidence=[
|
|
85
86
|
"README.md",
|
|
86
|
-
"README.md::The current release is **
|
|
87
|
-
"SECURITY.md::
|
|
88
|
-
"vscode-extension/README.md::**
|
|
89
|
-
"docs/CHANGELOG.md::## [
|
|
87
|
+
"README.md::The current release is **8.0.0",
|
|
88
|
+
"SECURITY.md::8.0.x (latest)",
|
|
89
|
+
"vscode-extension/README.md::**8.0.0",
|
|
90
|
+
"docs/CHANGELOG.md::## [8.0.0]",
|
|
90
91
|
"FEATURE_STATUS.md",
|
|
91
|
-
"
|
|
92
|
+
"RELEASE_NOTES_v8.0.0.md",
|
|
93
|
+
"latticeai/core/agent.py::SingleAgentRuntime",
|
|
94
|
+
"latticeai/core/agent.py::AgentRuntime = SingleAgentRuntime",
|
|
95
|
+
"lattice_brain/runtime/contracts.py::runtime-boundary/v1",
|
|
96
|
+
"lattice_brain/runtime/contracts.py::RuntimeBoundaryProtocol",
|
|
97
|
+
"lattice_brain/runtime/agent_runtime.py::def boundary",
|
|
98
|
+
"latticeai/core/agent.py::def boundary",
|
|
99
|
+
"latticeai/services/architecture_readiness.py::lattice-architecture-contract/v1",
|
|
100
|
+
"latticeai/services/tool_dispatch.py::rollback_file",
|
|
92
101
|
],
|
|
93
102
|
),
|
|
94
103
|
ProductGate(
|
|
@@ -8,11 +8,12 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
10
|
from pathlib import Path
|
|
11
|
+
import subprocess
|
|
11
12
|
from typing import Any, Callable, Dict, Mapping, Optional
|
|
12
13
|
|
|
13
14
|
from fastapi import HTTPException
|
|
14
15
|
|
|
15
|
-
from latticeai.core.agent import AgentDeps,
|
|
16
|
+
from latticeai.core.agent import AgentDeps, SingleAgentRuntime
|
|
16
17
|
from latticeai.core.agent_prompts import (
|
|
17
18
|
CRITIC_PROMPT,
|
|
18
19
|
EXECUTOR_PROMPT,
|
|
@@ -106,6 +107,16 @@ class ToolDispatchService:
|
|
|
106
107
|
detail=f"'{tool_name}' 툴은 관리자 전용입니다.",
|
|
107
108
|
)
|
|
108
109
|
|
|
110
|
+
def rollback_file(self, path: str) -> Dict[str, Any]:
|
|
111
|
+
r = subprocess.run(
|
|
112
|
+
["git", "checkout", "--", path],
|
|
113
|
+
cwd=str(AGENT_ROOT),
|
|
114
|
+
capture_output=True,
|
|
115
|
+
text=True,
|
|
116
|
+
timeout=10,
|
|
117
|
+
)
|
|
118
|
+
return {"path": path, "ok": r.returncode == 0, "stderr": r.stderr[:200]}
|
|
119
|
+
|
|
109
120
|
|
|
110
121
|
DEFAULT_TOOL_DISPATCH_SERVICE = ToolDispatchService()
|
|
111
122
|
|
|
@@ -185,7 +196,7 @@ def build_agent_runtime(
|
|
|
185
196
|
hooks: Any = None,
|
|
186
197
|
brain_memory: Any = None,
|
|
187
198
|
dispatch_service: ToolDispatchService = DEFAULT_TOOL_DISPATCH_SERVICE,
|
|
188
|
-
) ->
|
|
199
|
+
) -> SingleAgentRuntime:
|
|
189
200
|
ensure_agent_root()
|
|
190
201
|
deps = AgentDeps(
|
|
191
202
|
generate_as=model_router.generate_as,
|
|
@@ -205,10 +216,11 @@ def build_agent_runtime(
|
|
|
205
216
|
critic_prompt=CRITIC_PROMPT,
|
|
206
217
|
memory_updater_prompt=MEMORY_UPDATER_PROMPT,
|
|
207
218
|
agent_root=AGENT_ROOT,
|
|
219
|
+
rollback_file=dispatch_service.rollback_file,
|
|
208
220
|
hooks=hooks,
|
|
209
221
|
brain_memory=brain_memory,
|
|
210
222
|
)
|
|
211
|
-
return
|
|
223
|
+
return SingleAgentRuntime(deps)
|
|
212
224
|
|
|
213
225
|
|
|
214
226
|
def tool_response(fn, *args):
|
package/package.json
CHANGED
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED