ltcai 9.9.1 → 9.9.2
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 +15 -14
- package/docs/CHANGELOG.md +36 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chat.py +3 -0
- package/latticeai/api/chat_agent_http.py +2 -1
- package/latticeai/api/chat_intents.py +130 -7
- package/latticeai/app_factory.py +1 -0
- package/latticeai/core/agent.py +114 -2
- package/latticeai/core/file_generation.py +58 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/app_context.py +3 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/product_readiness.py +6 -1
- package/latticeai/services/tool_dispatch.py +36 -0
- package/package.json +1 -1
- package/scripts/check_current_release_docs.mjs +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 +27 -27
- package/static/app/assets/{Act-CcSWTQsx.js → Act-Cx_gZNbE.js} +1 -1
- package/static/app/assets/{Brain-CSCDZEHd.js → Brain-BnGbytHa.js} +1 -1
- package/static/app/assets/BrainHome-CRUMJL93.js +2 -0
- package/static/app/assets/{BrainSignals-CmFD8PWk.js → BrainSignals-BP8-Qh2g.js} +1 -1
- package/static/app/assets/{Capture-BiuerOio.js → Capture-CAKnLcb0.js} +1 -1
- package/static/app/assets/{CommandPalette-BrqpT7K2.js → CommandPalette-BoUTXBfm.js} +1 -1
- package/static/app/assets/{Library-BcaiPbum.js → Library-PcfVUco6.js} +1 -1
- package/static/app/assets/{LivingBrain-6FvA0H5T.js → LivingBrain-C3VbogVo.js} +1 -1
- package/static/app/assets/{ProductFlow-Cr8bkQPw.js → ProductFlow-BLau7c7N.js} +1 -1
- package/static/app/assets/{ReviewCard-D56TMypk.js → ReviewCard-CX5RqRqx.js} +1 -1
- package/static/app/assets/{System-D6uJacqu.js → System-DRHc_6Su.js} +1 -1
- package/static/app/assets/{bot-DgC7xgUl.js → bot-DZkDdh-A.js} +1 -1
- package/static/app/assets/{cpu-DDGb4XHl.js → cpu-DyvsDBbj.js} +1 -1
- package/static/app/assets/{download-C-Ei3GFa.js → download-C7yxYHvz.js} +1 -1
- package/static/app/assets/{folder-open-BbVuSO9M.js → folder-open-CffoIr9t.js} +1 -1
- package/static/app/assets/{hard-drive-yDZR6XYz.js → hard-drive-C1oojrHb.js} +1 -1
- package/static/app/assets/{index-DrZSkwrE.js → index-BS_3aN1n.js} +3 -3
- package/static/app/assets/index-DU78Kogz.css +2 -0
- package/static/app/assets/{input-DPIkTs9n.js → input-B_U7zvbR.js} +1 -1
- package/static/app/assets/{navigation-B1mkmyUe.js → navigation-xG-gyjbe.js} +1 -1
- package/static/app/assets/{network-0XDI1Uf_.js → network-BF13HJJF.js} +1 -1
- package/static/app/assets/{primitives-sIQYKyf5.js → primitives-DsuKh3M8.js} +1 -1
- package/static/app/assets/{sunrise-DXMjueec.js → sunrise-CIP2ELJ1.js} +1 -1
- package/static/app/assets/{textarea-DvnwpnqS.js → textarea-DsDL4ET8.js} +1 -1
- package/static/app/assets/utils-Dh-Dwtkb.js +7 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/BrainHome-B5QpJ1Fx.js +0 -2
- package/static/app/assets/index-CFRhNBuH.css +0 -2
- package/static/app/assets/utils-BrfFHAJl.js +0 -7
|
@@ -195,6 +195,14 @@ def validate_file_content(content: str, target_path: str) -> Tuple[bool, str]:
|
|
|
195
195
|
return False, "not a complete HTML document (missing <!DOCTYPE html>/<html>)"
|
|
196
196
|
if "</html>" not in lower:
|
|
197
197
|
return False, "HTML document is truncated (missing </html>)"
|
|
198
|
+
# A document that merely *contains* html somewhere is not a valid
|
|
199
|
+
# file payload: fenced/chat-wrapped replies must fail here so the
|
|
200
|
+
# extraction pass gets a chance to slice out the real document.
|
|
201
|
+
stripped = content.lstrip()
|
|
202
|
+
if not (stripped.lower().startswith("<!doctype") or stripped.lower().startswith("<html")):
|
|
203
|
+
return False, "HTML document is wrapped in prose or fences"
|
|
204
|
+
if "```" in content:
|
|
205
|
+
return False, "output still contains Markdown fences"
|
|
198
206
|
return True, "ok"
|
|
199
207
|
if ext == ".json":
|
|
200
208
|
try:
|
|
@@ -359,6 +367,54 @@ def _repair_html(salvage: str, user_request: str) -> str:
|
|
|
359
367
|
)
|
|
360
368
|
|
|
361
369
|
|
|
370
|
+
# Extensions the Brain UI can render inline (preview) after creation.
|
|
371
|
+
PREVIEWABLE_EXTENSIONS = frozenset({
|
|
372
|
+
".html", ".htm", ".md", ".markdown", ".txt", ".json", ".css", ".js",
|
|
373
|
+
".csv", ".py", ".yaml", ".yml", ".xml", ".sql", ".sh",
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ── write-side sanitize (ArtifactWritePipeline) ─────────────────────────
|
|
378
|
+
|
|
379
|
+
def sanitize_write_content(
|
|
380
|
+
target_path: str,
|
|
381
|
+
content: Any,
|
|
382
|
+
user_request: str = "",
|
|
383
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
384
|
+
"""Single write-side guarantee for model-produced file content.
|
|
385
|
+
|
|
386
|
+
The direct chat path already runs the full generate→validate→repair
|
|
387
|
+
pipeline, but the agent JSON loop historically wrote ``args.content``
|
|
388
|
+
verbatim — weak models routinely put fenced/chatty payloads there. This
|
|
389
|
+
conservative sanitizer closes that gap for *any* write entry point:
|
|
390
|
+
|
|
391
|
+
1. content that already validates is returned byte-for-byte unchanged
|
|
392
|
+
(trusted/user-authored content is never mangled);
|
|
393
|
+
2. otherwise the extraction pass strips fences/think-blocks/chat noise
|
|
394
|
+
and is used only when the extracted payload validates;
|
|
395
|
+
3. otherwise deterministic repair guarantees a structurally valid file.
|
|
396
|
+
|
|
397
|
+
Empty content is left untouched (creating an empty file is a legitimate,
|
|
398
|
+
intentional action — e.g. ``__init__.py``). Returns ``(content, meta)``
|
|
399
|
+
where meta is ``{"sanitized": bool, "repaired": bool, "reason": str}``.
|
|
400
|
+
"""
|
|
401
|
+
raw = str(content or "")
|
|
402
|
+
if not raw.strip():
|
|
403
|
+
return raw, {"sanitized": False, "repaired": False, "reason": "empty"}
|
|
404
|
+
ok, reason = validate_file_content(raw, target_path)
|
|
405
|
+
if ok:
|
|
406
|
+
return raw, {"sanitized": False, "repaired": False, "reason": "ok"}
|
|
407
|
+
extracted = extract_file_content(raw, target_path)
|
|
408
|
+
if extracted:
|
|
409
|
+
extracted_ok, _ = validate_file_content(extracted, target_path)
|
|
410
|
+
if extracted_ok:
|
|
411
|
+
return extracted, {"sanitized": True, "repaired": False, "reason": reason}
|
|
412
|
+
repaired = repair_file_content(
|
|
413
|
+
extracted or raw, target_path, user_request or f"content for {target_path}"
|
|
414
|
+
)
|
|
415
|
+
return repaired, {"sanitized": True, "repaired": True, "reason": reason}
|
|
416
|
+
|
|
417
|
+
|
|
362
418
|
# ── filename inference ──────────────────────────────────────────────────
|
|
363
419
|
|
|
364
420
|
_CREATE_VERB_RE = re.compile(
|
|
@@ -441,11 +497,13 @@ async def generate_file_content(
|
|
|
441
497
|
|
|
442
498
|
|
|
443
499
|
__all__ = [
|
|
500
|
+
"PREVIEWABLE_EXTENSIONS",
|
|
444
501
|
"build_file_generation_context",
|
|
445
502
|
"extract_file_content",
|
|
446
503
|
"generate_file_content",
|
|
447
504
|
"infer_file_target",
|
|
448
505
|
"looks_like_refusal",
|
|
449
506
|
"repair_file_content",
|
|
507
|
+
"sanitize_write_content",
|
|
450
508
|
"validate_file_content",
|
|
451
509
|
]
|
|
@@ -49,7 +49,7 @@ __all__ = [
|
|
|
49
49
|
"remove_skill_directory",
|
|
50
50
|
]
|
|
51
51
|
|
|
52
|
-
WORKSPACE_OS_VERSION = "9.9.
|
|
52
|
+
WORKSPACE_OS_VERSION = "9.9.2"
|
|
53
53
|
|
|
54
54
|
# Workspace types separate single-user Personal workspaces from shared
|
|
55
55
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|
|
@@ -34,6 +34,9 @@ class AppContext:
|
|
|
34
34
|
chat_service: Any = None
|
|
35
35
|
context_assembler: Any = None
|
|
36
36
|
brain_memory: Any = None
|
|
37
|
+
# Unified ingestion gateway (lattice_brain.ingestion.IngestionPipeline);
|
|
38
|
+
# None when the knowledge graph is disabled.
|
|
39
|
+
ingestion_pipeline: Any = None
|
|
37
40
|
chat_agent_runtime: Any = None
|
|
38
41
|
gardener: Any = None
|
|
39
42
|
hooks: Any = None
|
|
@@ -18,7 +18,7 @@ from typing import Any, Dict, List
|
|
|
18
18
|
|
|
19
19
|
from latticeai.services.architecture_readiness import architecture_readiness
|
|
20
20
|
|
|
21
|
-
PRODUCT_VERSION_TARGET = "9.9.
|
|
21
|
+
PRODUCT_VERSION_TARGET = "9.9.2"
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@dataclass(frozen=True)
|
|
@@ -60,6 +60,11 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
60
60
|
"latticeai/api/chat.py::is_file_action_request",
|
|
61
61
|
"latticeai/api/chat_intents.py::direct_file_action",
|
|
62
62
|
"latticeai/api/chat_intents.py::direct_write_file",
|
|
63
|
+
# v9.9.2 ArtifactWritePipeline: every write path shares the same
|
|
64
|
+
# extract → validate → repair guarantee, proven by the FG harness.
|
|
65
|
+
"latticeai/core/file_generation.py::sanitize_write_content",
|
|
66
|
+
"latticeai/core/agent.py::content_sanitize",
|
|
67
|
+
"tests/unit/test_artifact_write_scenarios.py::test_fg06_agent_dispatch_strips_fences_from_write_file_content",
|
|
63
68
|
"tests/unit/test_chat_telegram_decoupling.py::test_chat_file_creation_intent_writes_real_file",
|
|
64
69
|
],
|
|
65
70
|
),
|
|
@@ -307,6 +307,42 @@ def collect_created_files(transcript: list) -> list:
|
|
|
307
307
|
return files
|
|
308
308
|
|
|
309
309
|
|
|
310
|
+
def collect_artifacts(transcript: list) -> list:
|
|
311
|
+
"""Artifact-first view of a run: every produced file with honesty flags.
|
|
312
|
+
|
|
313
|
+
``repaired`` surfaces the ArtifactWritePipeline verdict recorded on the
|
|
314
|
+
transcript step (``content_sanitize``) so the UI can badge deterministic
|
|
315
|
+
scaffolds instead of presenting them as high-quality model output.
|
|
316
|
+
"""
|
|
317
|
+
from latticeai.core.file_generation import PREVIEWABLE_EXTENSIONS
|
|
318
|
+
|
|
319
|
+
artifacts = []
|
|
320
|
+
for step in transcript:
|
|
321
|
+
if step.get("action") not in FILE_CREATE_ACTIONS:
|
|
322
|
+
continue
|
|
323
|
+
result = step.get("result", {})
|
|
324
|
+
if not isinstance(result, dict):
|
|
325
|
+
continue
|
|
326
|
+
sanitize = step.get("content_sanitize") or {}
|
|
327
|
+
repaired = bool(sanitize.get("repaired"))
|
|
328
|
+
rel_paths = (
|
|
329
|
+
result["created_files"]
|
|
330
|
+
if isinstance(result.get("created_files"), list)
|
|
331
|
+
else ([result["path"]] if result.get("path") else [])
|
|
332
|
+
)
|
|
333
|
+
for rel_path in rel_paths:
|
|
334
|
+
artifacts.append({
|
|
335
|
+
"kind": "file",
|
|
336
|
+
"path": rel_path,
|
|
337
|
+
"filename": Path(rel_path).name,
|
|
338
|
+
"bytes": result.get("bytes", 0) if len(rel_paths) == 1 else 0,
|
|
339
|
+
"previewable": Path(rel_path).suffix.lower() in PREVIEWABLE_EXTENSIONS,
|
|
340
|
+
"valid": True,
|
|
341
|
+
"repaired": repaired,
|
|
342
|
+
})
|
|
343
|
+
return artifacts
|
|
344
|
+
|
|
345
|
+
|
|
310
346
|
def build_agent_runtime(
|
|
311
347
|
*,
|
|
312
348
|
model_router: Any,
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ const root = process.cwd();
|
|
|
6
6
|
const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
7
7
|
const version = pkg.version;
|
|
8
8
|
const releaseDir = `output/release/v${version}`;
|
|
9
|
-
const releaseTheme = "
|
|
9
|
+
const releaseTheme = "Artifact Trust";
|
|
10
10
|
const title = `${version} — ${releaseTheme}`;
|
|
11
11
|
const escapedVersion = version.replaceAll(".", "\\.");
|
|
12
12
|
|
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "9.9.
|
|
2
|
+
"version": "9.9.2",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
|
-
"app": "/static/app/assets/index-
|
|
5
|
+
"app": "/static/app/assets/index-BS_3aN1n.js"
|
|
6
6
|
},
|
|
7
7
|
"assets": {
|
|
8
8
|
"../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
|
|
9
|
-
"_BrainSignals-
|
|
10
|
-
"_LivingBrain-
|
|
11
|
-
"_ReviewCard-
|
|
12
|
-
"_bot-
|
|
13
|
-
"_cpu-
|
|
14
|
-
"_download-
|
|
15
|
-
"_folder-open-
|
|
16
|
-
"_hard-drive-
|
|
17
|
-
"_input-
|
|
18
|
-
"_navigation-
|
|
19
|
-
"_network-
|
|
20
|
-
"_primitives-
|
|
21
|
-
"_sunrise-
|
|
22
|
-
"_textarea-
|
|
23
|
-
"_utils-
|
|
24
|
-
"index.html": "/static/app/assets/index-
|
|
25
|
-
"assets/index-
|
|
26
|
-
"src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-
|
|
27
|
-
"src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-
|
|
28
|
-
"src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-
|
|
29
|
-
"src/pages/Act.tsx": "/static/app/assets/Act-
|
|
30
|
-
"src/pages/Brain.tsx": "/static/app/assets/Brain-
|
|
31
|
-
"src/pages/Capture.tsx": "/static/app/assets/Capture-
|
|
32
|
-
"src/pages/Library.tsx": "/static/app/assets/Library-
|
|
33
|
-
"src/pages/System.tsx": "/static/app/assets/System-
|
|
9
|
+
"_BrainSignals-BP8-Qh2g.js": "/static/app/assets/BrainSignals-BP8-Qh2g.js",
|
|
10
|
+
"_LivingBrain-C3VbogVo.js": "/static/app/assets/LivingBrain-C3VbogVo.js",
|
|
11
|
+
"_ReviewCard-CX5RqRqx.js": "/static/app/assets/ReviewCard-CX5RqRqx.js",
|
|
12
|
+
"_bot-DZkDdh-A.js": "/static/app/assets/bot-DZkDdh-A.js",
|
|
13
|
+
"_cpu-DyvsDBbj.js": "/static/app/assets/cpu-DyvsDBbj.js",
|
|
14
|
+
"_download-C7yxYHvz.js": "/static/app/assets/download-C7yxYHvz.js",
|
|
15
|
+
"_folder-open-CffoIr9t.js": "/static/app/assets/folder-open-CffoIr9t.js",
|
|
16
|
+
"_hard-drive-C1oojrHb.js": "/static/app/assets/hard-drive-C1oojrHb.js",
|
|
17
|
+
"_input-B_U7zvbR.js": "/static/app/assets/input-B_U7zvbR.js",
|
|
18
|
+
"_navigation-xG-gyjbe.js": "/static/app/assets/navigation-xG-gyjbe.js",
|
|
19
|
+
"_network-BF13HJJF.js": "/static/app/assets/network-BF13HJJF.js",
|
|
20
|
+
"_primitives-DsuKh3M8.js": "/static/app/assets/primitives-DsuKh3M8.js",
|
|
21
|
+
"_sunrise-CIP2ELJ1.js": "/static/app/assets/sunrise-CIP2ELJ1.js",
|
|
22
|
+
"_textarea-DsDL4ET8.js": "/static/app/assets/textarea-DsDL4ET8.js",
|
|
23
|
+
"_utils-Dh-Dwtkb.js": "/static/app/assets/utils-Dh-Dwtkb.js",
|
|
24
|
+
"index.html": "/static/app/assets/index-BS_3aN1n.js",
|
|
25
|
+
"assets/index-DU78Kogz.css": "/static/app/assets/index-DU78Kogz.css",
|
|
26
|
+
"src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-BLau7c7N.js",
|
|
27
|
+
"src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-CRUMJL93.js",
|
|
28
|
+
"src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-BoUTXBfm.js",
|
|
29
|
+
"src/pages/Act.tsx": "/static/app/assets/Act-Cx_gZNbE.js",
|
|
30
|
+
"src/pages/Brain.tsx": "/static/app/assets/Brain-BnGbytHa.js",
|
|
31
|
+
"src/pages/Capture.tsx": "/static/app/assets/Capture-CAKnLcb0.js",
|
|
32
|
+
"src/pages/Library.tsx": "/static/app/assets/Library-PcfVUco6.js",
|
|
33
|
+
"src/pages/System.tsx": "/static/app/assets/System-DRHc_6Su.js"
|
|
34
34
|
}
|
|
35
35
|
}
|