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.
Files changed (61) hide show
  1. package/README.md +15 -14
  2. package/docs/CHANGELOG.md +36 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/ONBOARDING.md +1 -1
  6. package/docs/OPERATIONS.md +1 -1
  7. package/docs/TRUST_MODEL.md +1 -1
  8. package/docs/WHY_LATTICE.md +1 -1
  9. package/docs/kg-schema.md +1 -1
  10. package/lattice_brain/__init__.py +1 -1
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/chat.py +3 -0
  14. package/latticeai/api/chat_agent_http.py +2 -1
  15. package/latticeai/api/chat_intents.py +130 -7
  16. package/latticeai/app_factory.py +1 -0
  17. package/latticeai/core/agent.py +114 -2
  18. package/latticeai/core/file_generation.py +58 -0
  19. package/latticeai/core/legacy_compatibility.py +1 -1
  20. package/latticeai/core/marketplace.py +1 -1
  21. package/latticeai/core/workspace_os.py +1 -1
  22. package/latticeai/services/app_context.py +3 -0
  23. package/latticeai/services/architecture_readiness.py +1 -1
  24. package/latticeai/services/product_readiness.py +6 -1
  25. package/latticeai/services/tool_dispatch.py +36 -0
  26. package/package.json +1 -1
  27. package/scripts/check_current_release_docs.mjs +1 -1
  28. package/src-tauri/Cargo.lock +1 -1
  29. package/src-tauri/Cargo.toml +1 -1
  30. package/src-tauri/tauri.conf.json +1 -1
  31. package/static/app/asset-manifest.json +27 -27
  32. package/static/app/assets/{Act-CcSWTQsx.js → Act-Cx_gZNbE.js} +1 -1
  33. package/static/app/assets/{Brain-CSCDZEHd.js → Brain-BnGbytHa.js} +1 -1
  34. package/static/app/assets/BrainHome-CRUMJL93.js +2 -0
  35. package/static/app/assets/{BrainSignals-CmFD8PWk.js → BrainSignals-BP8-Qh2g.js} +1 -1
  36. package/static/app/assets/{Capture-BiuerOio.js → Capture-CAKnLcb0.js} +1 -1
  37. package/static/app/assets/{CommandPalette-BrqpT7K2.js → CommandPalette-BoUTXBfm.js} +1 -1
  38. package/static/app/assets/{Library-BcaiPbum.js → Library-PcfVUco6.js} +1 -1
  39. package/static/app/assets/{LivingBrain-6FvA0H5T.js → LivingBrain-C3VbogVo.js} +1 -1
  40. package/static/app/assets/{ProductFlow-Cr8bkQPw.js → ProductFlow-BLau7c7N.js} +1 -1
  41. package/static/app/assets/{ReviewCard-D56TMypk.js → ReviewCard-CX5RqRqx.js} +1 -1
  42. package/static/app/assets/{System-D6uJacqu.js → System-DRHc_6Su.js} +1 -1
  43. package/static/app/assets/{bot-DgC7xgUl.js → bot-DZkDdh-A.js} +1 -1
  44. package/static/app/assets/{cpu-DDGb4XHl.js → cpu-DyvsDBbj.js} +1 -1
  45. package/static/app/assets/{download-C-Ei3GFa.js → download-C7yxYHvz.js} +1 -1
  46. package/static/app/assets/{folder-open-BbVuSO9M.js → folder-open-CffoIr9t.js} +1 -1
  47. package/static/app/assets/{hard-drive-yDZR6XYz.js → hard-drive-C1oojrHb.js} +1 -1
  48. package/static/app/assets/{index-DrZSkwrE.js → index-BS_3aN1n.js} +3 -3
  49. package/static/app/assets/index-DU78Kogz.css +2 -0
  50. package/static/app/assets/{input-DPIkTs9n.js → input-B_U7zvbR.js} +1 -1
  51. package/static/app/assets/{navigation-B1mkmyUe.js → navigation-xG-gyjbe.js} +1 -1
  52. package/static/app/assets/{network-0XDI1Uf_.js → network-BF13HJJF.js} +1 -1
  53. package/static/app/assets/{primitives-sIQYKyf5.js → primitives-DsuKh3M8.js} +1 -1
  54. package/static/app/assets/{sunrise-DXMjueec.js → sunrise-CIP2ELJ1.js} +1 -1
  55. package/static/app/assets/{textarea-DvnwpnqS.js → textarea-DsDL4ET8.js} +1 -1
  56. package/static/app/assets/utils-Dh-Dwtkb.js +7 -0
  57. package/static/app/index.html +4 -4
  58. package/static/sw.js +1 -1
  59. package/static/app/assets/BrainHome-B5QpJ1Fx.js +0 -2
  60. package/static/app/assets/index-CFRhNBuH.css +0 -2
  61. 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
  ]
@@ -14,7 +14,7 @@ from pathlib import Path
14
14
  from typing import Any, Dict, List
15
15
 
16
16
 
17
- LEGACY_COMPATIBILITY_VERSION = "9.9.1"
17
+ LEGACY_COMPATIBILITY_VERSION = "9.9.2"
18
18
 
19
19
 
20
20
  @dataclass(frozen=True)
@@ -11,7 +11,7 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "9.9.1"
14
+ MARKETPLACE_VERSION = "9.9.2"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent", "ingestion_bridge")
16
16
 
17
17
 
@@ -49,7 +49,7 @@ __all__ = [
49
49
  "remove_skill_directory",
50
50
  ]
51
51
 
52
- WORKSPACE_OS_VERSION = "9.9.1"
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
@@ -17,7 +17,7 @@ from typing import Any, Dict, List
17
17
  from latticeai.core.legacy_compatibility import legacy_shim_report
18
18
 
19
19
 
20
- ARCHITECTURE_VERSION_TARGET = "9.9.1"
20
+ ARCHITECTURE_VERSION_TARGET = "9.9.2"
21
21
 
22
22
  PREFERRED_REFACTORING_ORDER = [
23
23
  "agent-runtime",
@@ -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.1"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "9.9.1",
3
+ "version": "9.9.2",
4
4
  "description": "Lattice AI — local-first Digital Brain that keeps your knowledge durable across any AI model.",
5
5
  "homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
6
6
  "repository": {
@@ -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 = "Clean Foundations";
9
+ const releaseTheme = "Artifact Trust";
10
10
  const title = `${version} — ${releaseTheme}`;
11
11
  const escapedVersion = version.replaceAll(".", "\\.");
12
12
 
@@ -1584,7 +1584,7 @@ dependencies = [
1584
1584
 
1585
1585
  [[package]]
1586
1586
  name = "lattice-ai-desktop"
1587
- version = "9.9.1"
1587
+ version = "9.9.2"
1588
1588
  dependencies = [
1589
1589
  "plist",
1590
1590
  "serde",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lattice-ai-desktop"
3
- version = "9.9.1"
3
+ version = "9.9.2"
4
4
  description = "Lattice AI Digital Brain desktop shell"
5
5
  authors = ["TaeSoo Park"]
6
6
  edition = "2021"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Lattice AI",
4
- "version": "9.9.1",
4
+ "version": "9.9.2",
5
5
  "identifier": "ai.lattice.desktop",
6
6
  "build": {
7
7
  "beforeDevCommand": "npm run frontend:dev",
@@ -1,35 +1,35 @@
1
1
  {
2
- "version": "9.9.1",
2
+ "version": "9.9.2",
3
3
  "generated_at": "vite",
4
4
  "entrypoints": {
5
- "app": "/static/app/assets/index-DrZSkwrE.js"
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-CmFD8PWk.js": "/static/app/assets/BrainSignals-CmFD8PWk.js",
10
- "_LivingBrain-6FvA0H5T.js": "/static/app/assets/LivingBrain-6FvA0H5T.js",
11
- "_ReviewCard-D56TMypk.js": "/static/app/assets/ReviewCard-D56TMypk.js",
12
- "_bot-DgC7xgUl.js": "/static/app/assets/bot-DgC7xgUl.js",
13
- "_cpu-DDGb4XHl.js": "/static/app/assets/cpu-DDGb4XHl.js",
14
- "_download-C-Ei3GFa.js": "/static/app/assets/download-C-Ei3GFa.js",
15
- "_folder-open-BbVuSO9M.js": "/static/app/assets/folder-open-BbVuSO9M.js",
16
- "_hard-drive-yDZR6XYz.js": "/static/app/assets/hard-drive-yDZR6XYz.js",
17
- "_input-DPIkTs9n.js": "/static/app/assets/input-DPIkTs9n.js",
18
- "_navigation-B1mkmyUe.js": "/static/app/assets/navigation-B1mkmyUe.js",
19
- "_network-0XDI1Uf_.js": "/static/app/assets/network-0XDI1Uf_.js",
20
- "_primitives-sIQYKyf5.js": "/static/app/assets/primitives-sIQYKyf5.js",
21
- "_sunrise-DXMjueec.js": "/static/app/assets/sunrise-DXMjueec.js",
22
- "_textarea-DvnwpnqS.js": "/static/app/assets/textarea-DvnwpnqS.js",
23
- "_utils-BrfFHAJl.js": "/static/app/assets/utils-BrfFHAJl.js",
24
- "index.html": "/static/app/assets/index-DrZSkwrE.js",
25
- "assets/index-CFRhNBuH.css": "/static/app/assets/index-CFRhNBuH.css",
26
- "src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-Cr8bkQPw.js",
27
- "src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-B5QpJ1Fx.js",
28
- "src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-BrqpT7K2.js",
29
- "src/pages/Act.tsx": "/static/app/assets/Act-CcSWTQsx.js",
30
- "src/pages/Brain.tsx": "/static/app/assets/Brain-CSCDZEHd.js",
31
- "src/pages/Capture.tsx": "/static/app/assets/Capture-BiuerOio.js",
32
- "src/pages/Library.tsx": "/static/app/assets/Library-BcaiPbum.js",
33
- "src/pages/System.tsx": "/static/app/assets/System-D6uJacqu.js"
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
  }