ltcai 10.0.0 → 10.3.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 +43 -32
- package/docs/CHANGELOG.md +204 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/HYBRID_CLOUD_KG_STREAMING.md +101 -0
- package/docs/MYPY_BACKLOG.md +98 -0
- 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/archive.py +5 -4
- package/lattice_brain/conversations.py +14 -3
- package/lattice_brain/embeddings.py +12 -2
- package/lattice_brain/graph/_kg_common.py +5 -5
- package/lattice_brain/graph/_kg_fsutil.py +5 -4
- package/lattice_brain/graph/discovery.py +4 -3
- package/lattice_brain/graph/discovery_index.py +0 -1
- package/lattice_brain/graph/documents.py +3 -2
- package/lattice_brain/graph/fusion.py +4 -0
- package/lattice_brain/graph/ingest.py +12 -2
- package/lattice_brain/graph/projection.py +3 -2
- package/lattice_brain/graph/provenance.py +5 -3
- package/lattice_brain/graph/rerank.py +4 -1
- package/lattice_brain/graph/retrieval.py +0 -1
- package/lattice_brain/graph/retrieval_docgen.py +0 -1
- package/lattice_brain/graph/retrieval_reads.py +0 -1
- package/lattice_brain/graph/retrieval_vector.py +0 -1
- package/lattice_brain/graph/schema.py +4 -3
- package/lattice_brain/graph/store.py +18 -4
- package/lattice_brain/graph/write_master.py +46 -1
- package/lattice_brain/ingestion.py +4 -1
- package/lattice_brain/portability.py +5 -2
- package/lattice_brain/quality.py +12 -5
- package/lattice_brain/quiet.py +43 -0
- package/lattice_brain/runtime/agent_runtime.py +19 -10
- package/lattice_brain/runtime/hooks.py +3 -2
- package/lattice_brain/runtime/multi_agent.py +2 -3
- package/lattice_brain/sensitivity.py +94 -0
- package/lattice_brain/storage/base.py +30 -2
- package/lattice_brain/storage/migration.py +3 -2
- package/lattice_brain/storage/postgres.py +2 -2
- package/lattice_brain/storage/sqlite.py +6 -4
- package/lattice_brain/workflow.py +4 -2
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +1 -1
- package/latticeai/api/agents.py +4 -2
- package/latticeai/api/auth.py +5 -1
- package/latticeai/api/automation_intelligence.py +2 -1
- package/latticeai/api/browser.py +3 -2
- package/latticeai/api/chat.py +28 -17
- package/latticeai/api/chat_agent_http.py +22 -8
- package/latticeai/api/chat_contracts.py +4 -0
- package/latticeai/api/chat_documents.py +6 -2
- package/latticeai/api/chat_hybrid.py +82 -0
- package/latticeai/api/computer_use.py +8 -3
- package/latticeai/api/knowledge_graph.py +1 -1
- package/latticeai/api/mcp.py +4 -4
- package/latticeai/api/models.py +5 -2
- package/latticeai/api/network_boundary.py +220 -0
- package/latticeai/api/permissions.py +0 -1
- package/latticeai/api/realtime.py +1 -1
- package/latticeai/api/security_dashboard.py +1 -1
- package/latticeai/api/setup.py +16 -3
- package/latticeai/api/static_routes.py +2 -1
- package/latticeai/api/tools.py +12 -8
- package/latticeai/api/voice_capture.py +3 -1
- package/latticeai/api/workflow_designer.py +1 -1
- package/latticeai/api/workspace.py +1 -2
- package/latticeai/app_factory.py +174 -145
- package/latticeai/cli/entrypoint.py +6 -4
- package/latticeai/core/agent.py +55 -495
- package/latticeai/core/agent_eval.py +2 -2
- package/latticeai/core/agent_helpers.py +493 -0
- package/latticeai/core/agent_prompts.py +0 -1
- package/latticeai/core/agent_registry.py +3 -1
- package/latticeai/core/agent_state.py +41 -0
- package/latticeai/core/audit.py +1 -1
- package/latticeai/core/builtin_hooks.py +2 -1
- package/latticeai/core/config.py +0 -1
- package/latticeai/core/embedding_providers.py +12 -1
- package/latticeai/core/file_generation.py +3 -0
- package/latticeai/core/invitations.py +4 -1
- package/latticeai/core/io_utils.py +3 -1
- package/latticeai/core/legacy_compatibility.py +1 -2
- package/latticeai/core/local_embeddings.py +12 -1
- package/latticeai/core/marketplace.py +1 -2
- package/latticeai/core/mcp_registry.py +0 -1
- package/latticeai/core/model_compat.py +1 -1
- package/latticeai/core/model_resolution.py +1 -1
- package/latticeai/core/network_boundary.py +168 -0
- package/latticeai/core/oidc.py +3 -0
- package/latticeai/core/permission_mode.py +6 -6
- package/latticeai/core/plugins.py +3 -2
- package/latticeai/core/policy.py +0 -1
- package/latticeai/core/quiet.py +84 -0
- package/latticeai/core/realtime.py +3 -2
- package/latticeai/core/run_store.py +4 -2
- package/latticeai/core/security.py +4 -0
- package/latticeai/core/users.py +5 -3
- package/latticeai/core/workspace_graph_trace.py +3 -0
- package/latticeai/core/workspace_os.py +65 -273
- package/latticeai/core/workspace_os_constants.py +126 -0
- package/latticeai/core/workspace_os_state.py +180 -0
- package/latticeai/core/workspace_os_utils.py +3 -1
- package/latticeai/core/workspace_permissions.py +1 -0
- package/latticeai/core/workspace_snapshots.py +3 -1
- package/latticeai/core/workspace_timeline.py +3 -1
- package/latticeai/integrations/telegram_bot.py +25 -16
- package/latticeai/models/router.py +6 -3
- package/latticeai/runtime/access_runtime.py +3 -1
- package/latticeai/runtime/audit_runtime.py +3 -2
- package/latticeai/runtime/chat_wiring.py +4 -1
- package/latticeai/runtime/history_runtime.py +1 -1
- package/latticeai/runtime/history_writer.py +138 -0
- package/latticeai/runtime/lifespan_runtime.py +3 -1
- package/latticeai/runtime/network_boundary_wiring.py +124 -0
- package/latticeai/runtime/persistence_runtime.py +3 -1
- package/latticeai/runtime/router_registration.py +11 -1
- package/latticeai/services/architecture_readiness.py +1 -2
- package/latticeai/services/change_proposals.py +2 -1
- package/latticeai/services/cloud_egress_audit.py +85 -0
- package/latticeai/services/cloud_extraction.py +129 -0
- package/latticeai/services/cloud_streaming.py +266 -0
- package/latticeai/services/cloud_token_guard.py +84 -0
- package/latticeai/services/command_center.py +2 -1
- package/latticeai/services/folder_watch.py +3 -0
- package/latticeai/services/funnel_metrics.py +3 -1
- package/latticeai/services/hybrid_chat.py +265 -0
- package/latticeai/services/hybrid_context.py +228 -0
- package/latticeai/services/hybrid_policy.py +178 -0
- package/latticeai/services/memory_service.py +1 -1
- package/latticeai/services/model_catalog.py +10 -1
- package/latticeai/services/model_engines.py +35 -14
- package/latticeai/services/model_loading.py +3 -2
- package/latticeai/services/model_runtime.py +68 -17
- package/latticeai/services/multimodal_streaming.py +123 -0
- package/latticeai/services/network_boundary_service.py +154 -0
- package/latticeai/services/openai_compatible_adapter.py +100 -0
- package/latticeai/services/p_reinforce.py +4 -0
- package/latticeai/services/platform_runtime.py +9 -4
- package/latticeai/services/process_audit.py +0 -1
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/run_executor.py +3 -2
- package/latticeai/services/search_service.py +1 -4
- package/latticeai/services/setup_detection.py +2 -1
- package/latticeai/services/tool_dispatch.py +7 -2
- package/latticeai/services/upload_service.py +2 -1
- package/latticeai/setup/auto_setup.py +10 -7
- package/latticeai/setup/wizard.py +15 -11
- package/latticeai/tools/__init__.py +3 -3
- package/latticeai/tools/commands.py +7 -7
- package/latticeai/tools/computer.py +3 -2
- package/latticeai/tools/documents.py +10 -9
- package/latticeai/tools/filesystem.py +7 -4
- package/latticeai/tools/knowledge.py +2 -0
- package/latticeai/tools/local_files.py +1 -1
- package/latticeai/tools/network.py +2 -1
- package/package.json +7 -3
- package/scripts/bench_agent_smoke.py +0 -1
- package/scripts/bench_models.py +0 -1
- package/scripts/brain_quality_eval.py +7 -2
- package/scripts/bump_version.py +2 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/migrate_brain_storage.py +5 -1
- package/scripts/profile_kg.py +2 -7
- package/scripts/verify_hf_model_registry.py +2 -2
- 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 +37 -37
- package/static/app/assets/{Act-BtCREeN1.js → Act-DrKIGZeJ.js} +1 -1
- package/static/app/assets/{AdminConsole-TPeeN18T.js → AdminConsole-CEV1VefA.js} +1 -1
- package/static/app/assets/{Brain-BKs6JAp0.js → Brain-BZSXnwWU.js} +1 -1
- package/static/app/assets/{BrainHome-BPGOvSd6.js → BrainHome-CEBd86k6.js} +1 -1
- package/static/app/assets/{BrainSignals-CtzQZ15J.js → BrainSignals-O4s6R7av.js} +1 -1
- package/static/app/assets/{Capture-1_NaHWqB.js → Capture-AGbOxsg-.js} +1 -1
- package/static/app/assets/{CommandPalette-pqvQOXe4.js → CommandPalette-CeqBjCn2.js} +1 -1
- package/static/app/assets/Library-CXnbNiQW.js +1 -0
- package/static/app/assets/{LivingBrain-DlQ20Q75.js → LivingBrain-D9Iei2ZJ.js} +1 -1
- package/static/app/assets/{ProductFlow-BZvGDRi_.js → ProductFlow-JlXiDfMM.js} +1 -1
- package/static/app/assets/{ReviewCard-BWgI0D2s.js → ReviewCard-QQly2bTV.js} +1 -1
- package/static/app/assets/System-Bp9ZY4ER.js +1 -0
- package/static/app/assets/{activity-Dlfk8YC7.js → activity-CxRPOQ2F.js} +1 -1
- package/static/app/assets/{bot-CDvUB76P.js → bot-1bcsWB2s.js} +1 -1
- package/static/app/assets/{brain-xczrohrt.js → brain-CxaeKCRM.js} +1 -1
- package/static/app/assets/{button-SOdH3Oyf.js → button-ZYFqGan7.js} +1 -1
- package/static/app/assets/{circle-pause-CG1ythH4.js → circle-pause-B5NOIqvk.js} +1 -1
- package/static/app/assets/{circle-play-HXwvjS6W.js → circle-play-BiJVFXf7.js} +1 -1
- package/static/app/assets/{cpu-B0d-rGyk.js → cpu-Vvq0OKTr.js} +1 -1
- package/static/app/assets/{download-BGIkTQL6.js → download-SEcXzkx1.js} +1 -1
- package/static/app/assets/{folder-open-Dst_Z0_K.js → folder-open-Dw1ff_Qa.js} +1 -1
- package/static/app/assets/{hard-drive-D53MsWkV.js → hard-drive-B00fR7_r.js} +1 -1
- package/static/app/assets/index-CEkL4_M-.css +2 -0
- package/static/app/assets/{index-C_IrlQMV.js → index-Dm9VXl1o.js} +3 -3
- package/static/app/assets/{input-C5m0riF6.js → input-DtdWYQEa.js} +1 -1
- package/static/app/assets/{network-C5a-E5iS.js → network-CpjBu0kI.js} +1 -1
- package/static/app/assets/{primitives-vNXYf58F.js → primitives-CwsudBwc.js} +1 -1
- package/static/app/assets/search-CwEJWBEA.js +1 -0
- package/static/app/assets/{shield-alert-Cc-WVXqN.js → shield-alert-CpjBc-gQ.js} +1 -1
- package/static/app/assets/{textarea-BkZ0EqVO.js → textarea-DUPyjHQs.js} +1 -1
- package/static/app/assets/{useFocusTrap-CTtKbAOU.js → useFocusTrap-BWcMtCWH.js} +1 -1
- package/static/app/assets/{useQuery-Dx1fi4Wu.js → useQuery-Dg5fPqTS.js} +1 -1
- package/static/app/assets/{users-BFpQXtEF.js → users-BgcdTQGM.js} +1 -1
- package/static/app/assets/{utils-BA_lmW3J.js → utils-a3YXFtEO.js} +2 -2
- package/static/app/assets/workspace-BMUEiXUm.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/Library-DhvoPvC7.js +0 -1
- package/static/app/assets/System-CSMdYLMy.js +0 -1
- package/static/app/assets/index-FxDusbr0.css +0 -2
- package/static/app/assets/search-DhbSgW6m.js +0 -1
- package/static/app/assets/workspace-DBPB0jkX.js +0 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Persisted network-boundary preference for hybrid local + cloud turns.
|
|
2
|
+
|
|
3
|
+
Mirrors PermissionModeService:
|
|
4
|
+
|
|
5
|
+
* process default (env ``LATTICEAI_NETWORK_MODE`` or local_only)
|
|
6
|
+
* per-user override
|
|
7
|
+
* per-workspace override (wins over user)
|
|
8
|
+
|
|
9
|
+
Switching to ``cloud_allowed`` requires ``acknowledge_risk=True`` once; the
|
|
10
|
+
ack is audited. Hard node filters remain in ``latticeai.core.network_boundary``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import threading
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Callable, Dict, Optional
|
|
19
|
+
|
|
20
|
+
from latticeai.core.io_utils import atomic_write_json
|
|
21
|
+
from latticeai.core.network_boundary import (
|
|
22
|
+
DEFAULT_NETWORK_MODE,
|
|
23
|
+
NetworkBoundaryMode,
|
|
24
|
+
network_mode_catalog,
|
|
25
|
+
network_mode_contract,
|
|
26
|
+
normalize_network_mode,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class NetworkBoundaryService:
|
|
31
|
+
"""Load / save the network boundary dial."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
data_dir: Path,
|
|
37
|
+
default_mode: NetworkBoundaryMode | str = DEFAULT_NETWORK_MODE,
|
|
38
|
+
audit: Optional[Callable[..., None]] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._path = Path(data_dir) / "network_boundary.json"
|
|
41
|
+
self._default = normalize_network_mode(default_mode)
|
|
42
|
+
self._audit = audit or (lambda *a, **kw: None)
|
|
43
|
+
self._lock = threading.Lock()
|
|
44
|
+
|
|
45
|
+
def rebind_data_dir(self, data_dir: Path) -> None:
|
|
46
|
+
with self._lock:
|
|
47
|
+
self._path = Path(data_dir) / "network_boundary.json"
|
|
48
|
+
|
|
49
|
+
def rebind_audit(self, audit: Callable[..., None]) -> None:
|
|
50
|
+
with self._lock:
|
|
51
|
+
self._audit = audit
|
|
52
|
+
|
|
53
|
+
def _read(self) -> Dict[str, Any]:
|
|
54
|
+
if not self._path.exists():
|
|
55
|
+
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
56
|
+
try:
|
|
57
|
+
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
58
|
+
except Exception:
|
|
59
|
+
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
60
|
+
if not isinstance(data, dict):
|
|
61
|
+
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
62
|
+
data.setdefault("default", self._default.value)
|
|
63
|
+
data.setdefault("users", {})
|
|
64
|
+
data.setdefault("workspaces", {})
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
def _write(self, data: Dict[str, Any]) -> None:
|
|
68
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
atomic_write_json(self._path, data)
|
|
70
|
+
|
|
71
|
+
def _resolve_from(
|
|
72
|
+
self,
|
|
73
|
+
data: Dict[str, Any],
|
|
74
|
+
*,
|
|
75
|
+
user_email: Optional[str],
|
|
76
|
+
workspace_id: Optional[str],
|
|
77
|
+
) -> NetworkBoundaryMode:
|
|
78
|
+
if workspace_id:
|
|
79
|
+
ws = (data.get("workspaces") or {}).get(str(workspace_id))
|
|
80
|
+
if ws:
|
|
81
|
+
return normalize_network_mode(ws)
|
|
82
|
+
if user_email:
|
|
83
|
+
user = (data.get("users") or {}).get(str(user_email).lower())
|
|
84
|
+
if user:
|
|
85
|
+
return normalize_network_mode(user)
|
|
86
|
+
return normalize_network_mode(data.get("default") or self._default)
|
|
87
|
+
|
|
88
|
+
def resolve(
|
|
89
|
+
self,
|
|
90
|
+
*,
|
|
91
|
+
user_email: Optional[str] = None,
|
|
92
|
+
workspace_id: Optional[str] = None,
|
|
93
|
+
) -> NetworkBoundaryMode:
|
|
94
|
+
with self._lock:
|
|
95
|
+
data = self._read()
|
|
96
|
+
return self._resolve_from(
|
|
97
|
+
data, user_email=user_email, workspace_id=workspace_id,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def get(
|
|
101
|
+
self,
|
|
102
|
+
*,
|
|
103
|
+
user_email: Optional[str] = None,
|
|
104
|
+
workspace_id: Optional[str] = None,
|
|
105
|
+
) -> Dict[str, Any]:
|
|
106
|
+
mode = self.resolve(user_email=user_email, workspace_id=workspace_id)
|
|
107
|
+
contract = network_mode_contract(mode)
|
|
108
|
+
contract["catalog"] = network_mode_catalog()
|
|
109
|
+
contract["scope"] = {
|
|
110
|
+
"user_email": user_email,
|
|
111
|
+
"workspace_id": workspace_id,
|
|
112
|
+
}
|
|
113
|
+
return contract
|
|
114
|
+
|
|
115
|
+
def set_mode(
|
|
116
|
+
self,
|
|
117
|
+
mode: NetworkBoundaryMode | str,
|
|
118
|
+
*,
|
|
119
|
+
user_email: Optional[str] = None,
|
|
120
|
+
workspace_id: Optional[str] = None,
|
|
121
|
+
acknowledge_risk: bool = False,
|
|
122
|
+
source: str = "api",
|
|
123
|
+
) -> Dict[str, Any]:
|
|
124
|
+
mode = normalize_network_mode(mode)
|
|
125
|
+
if mode == NetworkBoundaryMode.CLOUD_ALLOWED and not acknowledge_risk:
|
|
126
|
+
raise PermissionError(
|
|
127
|
+
"cloud_allowed mode requires acknowledge_risk=true "
|
|
128
|
+
"(minimal related Knowledge Graph nodes may leave this machine)"
|
|
129
|
+
)
|
|
130
|
+
with self._lock:
|
|
131
|
+
data = self._read()
|
|
132
|
+
previous = self._resolve_from(
|
|
133
|
+
data, user_email=user_email, workspace_id=workspace_id,
|
|
134
|
+
)
|
|
135
|
+
if workspace_id:
|
|
136
|
+
data.setdefault("workspaces", {})[str(workspace_id)] = mode.value
|
|
137
|
+
elif user_email:
|
|
138
|
+
data.setdefault("users", {})[str(user_email).lower()] = mode.value
|
|
139
|
+
else:
|
|
140
|
+
data["default"] = mode.value
|
|
141
|
+
self._write(data)
|
|
142
|
+
self._audit(
|
|
143
|
+
"network_boundary_changed",
|
|
144
|
+
user_email=user_email,
|
|
145
|
+
workspace_id=workspace_id,
|
|
146
|
+
previous=previous.value,
|
|
147
|
+
mode=mode.value,
|
|
148
|
+
source=source,
|
|
149
|
+
acknowledge_risk=bool(acknowledge_risk),
|
|
150
|
+
)
|
|
151
|
+
return self.get(user_email=user_email, workspace_id=workspace_id)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
__all__ = ["NetworkBoundaryService"]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""OpenAI-compatible streaming adapter for hybrid cloud turns (Phase 1).
|
|
2
|
+
|
|
3
|
+
Uses the already-declared ``openai`` dependency. Configuration is entirely
|
|
4
|
+
environment-driven so no secrets land in the repo:
|
|
5
|
+
|
|
6
|
+
* ``LATTICEAI_CLOUD_API_KEY`` (required to actually call)
|
|
7
|
+
* ``LATTICEAI_CLOUD_BASE_URL`` (optional; default OpenAI)
|
|
8
|
+
* ``LATTICEAI_CLOUD_MODEL`` (optional; default gpt-4o-mini)
|
|
9
|
+
|
|
10
|
+
When the API key is missing the adapter raises a clear error so the bridge
|
|
11
|
+
can surface an honest message instead of silently failing.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
from typing import AsyncIterator, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OpenAICompatibleAdapter:
|
|
21
|
+
"""Minimal Chat Completions streaming adapter."""
|
|
22
|
+
|
|
23
|
+
provider_name = "openai_compatible"
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
*,
|
|
28
|
+
api_key: Optional[str] = None,
|
|
29
|
+
base_url: Optional[str] = None,
|
|
30
|
+
default_model: Optional[str] = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
self.api_key = (api_key or os.environ.get("LATTICEAI_CLOUD_API_KEY") or "").strip()
|
|
33
|
+
self.base_url = (
|
|
34
|
+
base_url or os.environ.get("LATTICEAI_CLOUD_BASE_URL") or ""
|
|
35
|
+
).strip() or None
|
|
36
|
+
self.default_model = (
|
|
37
|
+
default_model
|
|
38
|
+
or os.environ.get("LATTICEAI_CLOUD_MODEL")
|
|
39
|
+
or "gpt-4o-mini"
|
|
40
|
+
).strip()
|
|
41
|
+
|
|
42
|
+
def _client(self):
|
|
43
|
+
if not self.api_key:
|
|
44
|
+
raise RuntimeError(
|
|
45
|
+
"Cloud adapter is not configured. Set LATTICEAI_CLOUD_API_KEY "
|
|
46
|
+
"(and optionally LATTICEAI_CLOUD_BASE_URL / LATTICEAI_CLOUD_MODEL)."
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
from openai import AsyncOpenAI
|
|
50
|
+
except ImportError as exc: # pragma: no cover
|
|
51
|
+
raise RuntimeError(
|
|
52
|
+
"openai package is required for the cloud streaming adapter"
|
|
53
|
+
) from exc
|
|
54
|
+
kwargs = {"api_key": self.api_key}
|
|
55
|
+
if self.base_url:
|
|
56
|
+
kwargs["base_url"] = self.base_url
|
|
57
|
+
return AsyncOpenAI(**kwargs)
|
|
58
|
+
|
|
59
|
+
async def stream(
|
|
60
|
+
self,
|
|
61
|
+
*,
|
|
62
|
+
system: str,
|
|
63
|
+
user: str,
|
|
64
|
+
context: str,
|
|
65
|
+
model: Optional[str] = None,
|
|
66
|
+
) -> AsyncIterator[str]:
|
|
67
|
+
client = self._client()
|
|
68
|
+
chosen = (model or self.default_model).strip()
|
|
69
|
+
messages = [
|
|
70
|
+
{"role": "system", "content": system},
|
|
71
|
+
]
|
|
72
|
+
if context:
|
|
73
|
+
messages.append(
|
|
74
|
+
{
|
|
75
|
+
"role": "system",
|
|
76
|
+
"content": (
|
|
77
|
+
"Local Knowledge Graph context (minimal related nodes only):\n"
|
|
78
|
+
f"{context}"
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
messages.append({"role": "user", "content": user})
|
|
83
|
+
|
|
84
|
+
stream = await client.chat.completions.create(
|
|
85
|
+
model=chosen,
|
|
86
|
+
messages=messages,
|
|
87
|
+
stream=True,
|
|
88
|
+
temperature=0.2,
|
|
89
|
+
)
|
|
90
|
+
async for event in stream:
|
|
91
|
+
try:
|
|
92
|
+
delta = event.choices[0].delta
|
|
93
|
+
piece = getattr(delta, "content", None) or ""
|
|
94
|
+
except Exception: # noqa: BLE001
|
|
95
|
+
piece = ""
|
|
96
|
+
if piece:
|
|
97
|
+
yield piece
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = ["OpenAICompatibleAdapter"]
|
|
@@ -17,6 +17,8 @@ from datetime import datetime
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Any, Optional
|
|
19
19
|
|
|
20
|
+
from latticeai.core.quiet import quiet
|
|
21
|
+
|
|
20
22
|
BRAIN_DIR = Path(
|
|
21
23
|
os.getenv("LATTICEAI_OBSIDIAN_VAULT_DIR")
|
|
22
24
|
or os.getenv("LATTICEAI_BRAIN_DIR")
|
|
@@ -213,6 +215,7 @@ class PReinforceGardener:
|
|
|
213
215
|
"modified_at": datetime.fromtimestamp(stat.st_mtime).isoformat(timespec="seconds"),
|
|
214
216
|
})
|
|
215
217
|
except OSError:
|
|
218
|
+
quiet()
|
|
216
219
|
continue
|
|
217
220
|
folders.append({"name": folder, "description": desc, "files": files, "count": len(files)})
|
|
218
221
|
return {"root": str(BRAIN_DIR), "folders": folders}
|
|
@@ -273,5 +276,6 @@ class PReinforceGardener:
|
|
|
273
276
|
if len(results) >= limit:
|
|
274
277
|
break
|
|
275
278
|
except Exception:
|
|
279
|
+
quiet()
|
|
276
280
|
continue
|
|
277
281
|
return "\n\n".join(results)
|
|
@@ -18,8 +18,13 @@ from typing import Any, Callable, Dict, Optional, Set
|
|
|
18
18
|
from fastapi import HTTPException, Request
|
|
19
19
|
|
|
20
20
|
from lattice_brain.runtime.hooks import dispatch_tool
|
|
21
|
-
from lattice_brain.runtime.multi_agent import
|
|
21
|
+
from lattice_brain.runtime.multi_agent import (
|
|
22
|
+
MultiAgentOrchestrator,
|
|
23
|
+
default_role_runner,
|
|
24
|
+
llm_role_runner,
|
|
25
|
+
)
|
|
22
26
|
from lattice_brain.workflow import ApprovalRequired, WorkflowEngine
|
|
27
|
+
from latticeai.core.quiet import quiet
|
|
23
28
|
from latticeai.core.tool_registry import SCOPED_KNOWLEDGE_TOOLS
|
|
24
29
|
from latticeai.services.tool_dispatch import enforce_tool_policy
|
|
25
30
|
from latticeai.tools import execute_tool
|
|
@@ -178,7 +183,7 @@ class PlatformRuntime:
|
|
|
178
183
|
except Exception:
|
|
179
184
|
# Recall is an enrichment seam; the legacy store fallback
|
|
180
185
|
# keeps agent execution available if it degrades.
|
|
181
|
-
|
|
186
|
+
quiet()
|
|
182
187
|
try:
|
|
183
188
|
mems = self.store.search_memories(goal, user_email=user, workspace_id=scope).get("memories", [])
|
|
184
189
|
ctx = [str(m.get("content") or "")[:180] for m in mems[:6]]
|
|
@@ -188,7 +193,7 @@ class PlatformRuntime:
|
|
|
188
193
|
synth = [str(m.get("content") or "")[:160] for m in allm if "agent-synthesis" in (m.get("tags") or [])][:3]
|
|
189
194
|
ctx = synth + ctx
|
|
190
195
|
except Exception:
|
|
191
|
-
|
|
196
|
+
quiet()
|
|
192
197
|
if not ctx:
|
|
193
198
|
try:
|
|
194
199
|
recent = self.store.list_memories(user_email=user, workspace_id=scope).get("memories", [])
|
|
@@ -196,7 +201,7 @@ class PlatformRuntime:
|
|
|
196
201
|
# itself in the knowledge that just entered the Brain.
|
|
197
202
|
ctx = [str(m.get("content") or "")[:180] for m in recent[:8]]
|
|
198
203
|
except Exception:
|
|
199
|
-
|
|
204
|
+
quiet()
|
|
200
205
|
return ctx[:8]
|
|
201
206
|
except Exception:
|
|
202
207
|
return []
|
|
@@ -16,6 +16,7 @@ from lattice_brain.runtime.statuses import (
|
|
|
16
16
|
RUN_ACTIVE_STATUSES as ACTIVE_STATUSES,
|
|
17
17
|
)
|
|
18
18
|
from lattice_brain.workflow import WorkflowEngine
|
|
19
|
+
from latticeai.core.quiet import quiet
|
|
19
20
|
from latticeai.core.timeutil import now_iso as _now
|
|
20
21
|
|
|
21
22
|
|
|
@@ -144,7 +145,7 @@ class RunExecutor:
|
|
|
144
145
|
try:
|
|
145
146
|
self.append_audit_event("agent_run_failed", user_email=user_email, run_id=run_id, error=str(exc))
|
|
146
147
|
except Exception:
|
|
147
|
-
|
|
148
|
+
quiet()
|
|
148
149
|
finally:
|
|
149
150
|
self._handles.pop(run_id, None)
|
|
150
151
|
|
|
@@ -299,7 +300,7 @@ class RunExecutor:
|
|
|
299
300
|
workspace_id=workspace_id,
|
|
300
301
|
)
|
|
301
302
|
except Exception:
|
|
302
|
-
|
|
303
|
+
quiet()
|
|
303
304
|
|
|
304
305
|
def _execute_workflow_sync(
|
|
305
306
|
self,
|
|
@@ -13,7 +13,6 @@ from typing import Any, Dict, List, Mapping, Optional
|
|
|
13
13
|
from lattice_brain.graph._kg_fsutil import _parse_iso, _recency_score
|
|
14
14
|
from lattice_brain.graph.retrieval_policy import resolve_policy
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
DEFAULT_HYBRID_WEIGHTS = {
|
|
18
17
|
"keyword": 0.35,
|
|
19
18
|
"vector": 0.40,
|
|
@@ -70,9 +69,7 @@ class SearchService:
|
|
|
70
69
|
if not node_id or node_id not in scopes:
|
|
71
70
|
continue
|
|
72
71
|
scope = scopes[node_id]
|
|
73
|
-
if scope is None and include_legacy_global:
|
|
74
|
-
visible.append(item)
|
|
75
|
-
elif scope is not None and str(scope) in allowed:
|
|
72
|
+
if scope is None and include_legacy_global or scope is not None and str(scope) in allowed:
|
|
76
73
|
visible.append(item)
|
|
77
74
|
return visible
|
|
78
75
|
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import re
|
|
7
7
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
|
8
8
|
|
|
9
|
+
from latticeai.core.quiet import quiet
|
|
9
10
|
|
|
10
11
|
WhichFn = Callable[[str], Optional[str]]
|
|
11
12
|
RunFn = Callable[[List[str]], str]
|
|
@@ -32,7 +33,7 @@ def parse_windows_video_controllers(raw: str) -> List[Dict[str, Any]]:
|
|
|
32
33
|
if controllers:
|
|
33
34
|
return controllers
|
|
34
35
|
except Exception:
|
|
35
|
-
|
|
36
|
+
quiet()
|
|
36
37
|
current: Dict[str, Any] = {}
|
|
37
38
|
for line in raw.splitlines():
|
|
38
39
|
if line.startswith("Name="):
|
|
@@ -6,9 +6,9 @@ tool-response shaping are owned outside ``server_app``.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import subprocess
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from pathlib import Path
|
|
11
|
-
import subprocess
|
|
12
12
|
from typing import Any, Callable, Dict, Mapping, Optional
|
|
13
13
|
|
|
14
14
|
from fastapi import HTTPException
|
|
@@ -30,7 +30,12 @@ from latticeai.core.permission_mode import (
|
|
|
30
30
|
from latticeai.core.policy import role_has_capability
|
|
31
31
|
from latticeai.core.tool_governor import classify_tool_call
|
|
32
32
|
from latticeai.core.tool_registry import ToolPermission, ToolPolicy
|
|
33
|
-
from latticeai.tools import
|
|
33
|
+
from latticeai.tools import (
|
|
34
|
+
AGENT_ROOT,
|
|
35
|
+
DEFAULT_TOOL_REGISTRY,
|
|
36
|
+
ToolError,
|
|
37
|
+
ensure_agent_root,
|
|
38
|
+
)
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
def _default_load_users() -> Dict[str, Any]:
|
|
@@ -11,6 +11,7 @@ from typing import Optional
|
|
|
11
11
|
from fastapi import HTTPException, Request, UploadFile
|
|
12
12
|
|
|
13
13
|
from lattice_brain.ingestion import IngestionItem
|
|
14
|
+
from latticeai.core.quiet import quiet
|
|
14
15
|
from latticeai.tools import ToolError, read_document
|
|
15
16
|
|
|
16
17
|
|
|
@@ -161,7 +162,7 @@ async def process_uploaded_document(
|
|
|
161
162
|
try:
|
|
162
163
|
Path(tmp_path).unlink()
|
|
163
164
|
except OSError:
|
|
164
|
-
|
|
165
|
+
quiet()
|
|
165
166
|
|
|
166
167
|
# ── post_upload hook ── the whole upload → parse → index pipeline finished.
|
|
167
168
|
if hooks is not None:
|
|
@@ -47,6 +47,7 @@ from dataclasses import asdict, dataclass, field
|
|
|
47
47
|
from pathlib import Path
|
|
48
48
|
from typing import Any, Dict, List, Optional, Tuple
|
|
49
49
|
|
|
50
|
+
from latticeai.core.quiet import quiet
|
|
50
51
|
from latticeai.services.process_audit import (
|
|
51
52
|
CommandConfirmationError,
|
|
52
53
|
append_process_audit_event,
|
|
@@ -58,6 +59,8 @@ from latticeai.services.setup_detection import (
|
|
|
58
59
|
detect_cuda,
|
|
59
60
|
detect_tools,
|
|
60
61
|
detect_wsl_from_text,
|
|
62
|
+
)
|
|
63
|
+
from latticeai.services.setup_detection import (
|
|
61
64
|
parse_windows_video_controllers as _parse_windows_video_controllers,
|
|
62
65
|
)
|
|
63
66
|
|
|
@@ -177,7 +180,7 @@ def _detect_gpu(prof_os: str, arch: str) -> GPUInfo:
|
|
|
177
180
|
gpu.vram_mb = int(float(mem))
|
|
178
181
|
gpu.sdk.append("cuda")
|
|
179
182
|
except ValueError:
|
|
180
|
-
|
|
183
|
+
quiet()
|
|
181
184
|
|
|
182
185
|
# Apple Silicon / Metal
|
|
183
186
|
if prof_os == "darwin":
|
|
@@ -268,7 +271,7 @@ def _detect_cpu_details(prof_os: str) -> Tuple[str, int, int, List[str]]:
|
|
|
268
271
|
physical = int((_run(["sysctl", "-n", "hw.physicalcpu"]).strip() or physical))
|
|
269
272
|
logical = int((_run(["sysctl", "-n", "hw.logicalcpu"]).strip() or logical))
|
|
270
273
|
except ValueError:
|
|
271
|
-
|
|
274
|
+
quiet()
|
|
272
275
|
flags = [item.lower() for item in _run(["sysctl", "-n", "machdep.cpu.features"]).split()]
|
|
273
276
|
elif prof_os == "linux":
|
|
274
277
|
text = _read_text("/proc/cpuinfo")
|
|
@@ -287,19 +290,19 @@ def _detect_cpu_details(prof_os: str) -> Tuple[str, int, int, List[str]]:
|
|
|
287
290
|
try:
|
|
288
291
|
physical = int(value.strip())
|
|
289
292
|
except ValueError:
|
|
290
|
-
|
|
293
|
+
quiet()
|
|
291
294
|
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
292
295
|
try:
|
|
293
296
|
logical = int(value.strip())
|
|
294
297
|
except ValueError:
|
|
295
|
-
|
|
298
|
+
quiet()
|
|
296
299
|
try:
|
|
297
300
|
import ctypes
|
|
298
301
|
kernel32 = ctypes.windll.kernel32
|
|
299
302
|
feature_map = {6: "sse", 10: "sse2", 13: "sse3", 19: "neon", 28: "rdrand"}
|
|
300
303
|
flags.extend(name for code, name in feature_map.items() if kernel32.IsProcessorFeaturePresent(code))
|
|
301
304
|
except Exception:
|
|
302
|
-
|
|
305
|
+
quiet()
|
|
303
306
|
interesting = {"avx", "avx2", "avx512f", "fma", "neon", "sse4_2", "sse", "sse2", "sse3", "rdrand"}
|
|
304
307
|
return model, physical, logical, sorted({flag for flag in flags if flag in interesting})
|
|
305
308
|
|
|
@@ -368,14 +371,14 @@ def probe() -> SystemProfile:
|
|
|
368
371
|
prof.ram_mb = int(line.split("=", 1)[-1].strip()) // (1024 * 1024)
|
|
369
372
|
break
|
|
370
373
|
except Exception:
|
|
371
|
-
|
|
374
|
+
quiet()
|
|
372
375
|
|
|
373
376
|
# Disk
|
|
374
377
|
try:
|
|
375
378
|
usage = shutil.disk_usage(Path.home())
|
|
376
379
|
prof.disk_free_mb = usage.free // (1024 * 1024)
|
|
377
380
|
except Exception:
|
|
378
|
-
|
|
381
|
+
quiet()
|
|
379
382
|
|
|
380
383
|
prof.gpu = _detect_gpu(prof.os, prof.arch)
|
|
381
384
|
prof.package_manager = _detect_package_manager(prof.os)
|
|
@@ -21,6 +21,7 @@ import time
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Any, AsyncIterator, Dict, List, Tuple
|
|
23
23
|
|
|
24
|
+
from latticeai.core.quiet import quiet
|
|
24
25
|
from latticeai.services.process_audit import (
|
|
25
26
|
CommandConfirmationError,
|
|
26
27
|
append_process_audit_event,
|
|
@@ -32,6 +33,8 @@ from latticeai.services.setup_detection import (
|
|
|
32
33
|
detect_cuda,
|
|
33
34
|
detect_tools,
|
|
34
35
|
detect_wsl_from_text,
|
|
36
|
+
)
|
|
37
|
+
from latticeai.services.setup_detection import (
|
|
35
38
|
parse_windows_video_controllers as _parse_windows_video_controllers,
|
|
36
39
|
)
|
|
37
40
|
|
|
@@ -304,7 +307,7 @@ def _detect_chip() -> Dict[str, Any]:
|
|
|
304
307
|
brand = line.split(":", 1)[-1].strip()
|
|
305
308
|
break
|
|
306
309
|
except Exception:
|
|
307
|
-
|
|
310
|
+
quiet()
|
|
308
311
|
name = brand or platform.processor() or "Unknown CPU"
|
|
309
312
|
|
|
310
313
|
return {"name": name, "arch": arch, "is_apple_silicon": is_apple, "gen": gen}
|
|
@@ -321,7 +324,7 @@ def _detect_cpu() -> Dict[str, Any]:
|
|
|
321
324
|
physical_cores = int(_cmd(["sysctl", "-n", "hw.physicalcpu"], timeout=5) or physical_cores)
|
|
322
325
|
logical_cores = int(_cmd(["sysctl", "-n", "hw.logicalcpu"], timeout=5) or logical_cores)
|
|
323
326
|
except ValueError:
|
|
324
|
-
|
|
327
|
+
quiet()
|
|
325
328
|
elif platform.system() == "Linux":
|
|
326
329
|
try:
|
|
327
330
|
text = Path("/proc/cpuinfo").read_text(encoding="utf-8", errors="replace")
|
|
@@ -330,7 +333,7 @@ def _detect_cpu() -> Dict[str, Any]:
|
|
|
330
333
|
flags = line.split(":", 1)[-1].strip().lower().split()
|
|
331
334
|
break
|
|
332
335
|
except Exception:
|
|
333
|
-
|
|
336
|
+
quiet()
|
|
334
337
|
elif platform.system() == "Windows":
|
|
335
338
|
raw = _cmd(["wmic", "cpu", "get", "Name,NumberOfCores,NumberOfLogicalProcessors", "/format:list"], timeout=5)
|
|
336
339
|
for line in raw.splitlines():
|
|
@@ -341,12 +344,12 @@ def _detect_cpu() -> Dict[str, Any]:
|
|
|
341
344
|
try:
|
|
342
345
|
physical_cores = int(value.strip())
|
|
343
346
|
except ValueError:
|
|
344
|
-
|
|
347
|
+
quiet()
|
|
345
348
|
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
346
349
|
try:
|
|
347
350
|
logical_cores = int(value.strip())
|
|
348
351
|
except ValueError:
|
|
349
|
-
|
|
352
|
+
quiet()
|
|
350
353
|
try:
|
|
351
354
|
import ctypes
|
|
352
355
|
kernel32 = ctypes.windll.kernel32
|
|
@@ -359,7 +362,7 @@ def _detect_cpu() -> Dict[str, Any]:
|
|
|
359
362
|
}
|
|
360
363
|
flags.extend(name for code, name in feature_map.items() if kernel32.IsProcessorFeaturePresent(code))
|
|
361
364
|
except Exception:
|
|
362
|
-
|
|
365
|
+
quiet()
|
|
363
366
|
interesting = {"avx", "avx2", "avx512f", "fma", "neon", "sse4_2"}
|
|
364
367
|
if platform.system() == "Windows":
|
|
365
368
|
interesting.update({"sse", "sse2", "sse3", "rdrand"})
|
|
@@ -384,7 +387,7 @@ def _detect_ram_gb() -> float:
|
|
|
384
387
|
try:
|
|
385
388
|
return round(int(raw) / 1_073_741_824, 1)
|
|
386
389
|
except ValueError:
|
|
387
|
-
|
|
390
|
+
quiet()
|
|
388
391
|
if platform.system() == "Darwin":
|
|
389
392
|
profiler = _cmd(["system_profiler", "SPHardwareDataType"], timeout=8)
|
|
390
393
|
m = re.search(r"Memory:\s+([\d.]+)\s*(TB|GB|MB)", profiler, re.IGNORECASE)
|
|
@@ -406,7 +409,7 @@ def _detect_ram_gb() -> float:
|
|
|
406
409
|
if line.startswith("MemTotal:"):
|
|
407
410
|
return round(int(line.split()[1]) / 1_048_576, 1)
|
|
408
411
|
except Exception:
|
|
409
|
-
|
|
412
|
+
quiet()
|
|
410
413
|
return 0.0
|
|
411
414
|
|
|
412
415
|
def _detect_disk_free_gb() -> float:
|
|
@@ -427,6 +430,7 @@ def _detect_gpu() -> Dict[str, Any]:
|
|
|
427
430
|
name, mem = [part.strip() for part in line.split(",", 1)]
|
|
428
431
|
devices.append({"vendor": "nvidia", "name": name, "vram_mb": int(float(mem)), "backend": "cuda"})
|
|
429
432
|
except Exception:
|
|
433
|
+
quiet()
|
|
430
434
|
continue
|
|
431
435
|
|
|
432
436
|
if platform.system() == "Windows":
|
|
@@ -492,7 +496,7 @@ def _detect_wsl() -> Dict[str, Any]:
|
|
|
492
496
|
try:
|
|
493
497
|
raw = Path("/proc/version").read_text(encoding="utf-8", errors="replace")
|
|
494
498
|
except Exception:
|
|
495
|
-
|
|
499
|
+
quiet()
|
|
496
500
|
is_wsl, version = detect_wsl_from_text(platform.system().lower(), raw)
|
|
497
501
|
return {"is_wsl": is_wsl, "version": version}
|
|
498
502
|
|
|
@@ -1243,7 +1247,7 @@ def open_url(url: str) -> None:
|
|
|
1243
1247
|
command = ["os.startfile", url]
|
|
1244
1248
|
plan = command_plan(command, name="open_url", purpose="setup_wizard_open_url")
|
|
1245
1249
|
append_process_audit_event("setup_wizard_open_url", plan=plan, status="started")
|
|
1246
|
-
os.startfile(url) # type: ignore[attr-defined]
|
|
1250
|
+
os.startfile(url) # type: ignore[attr-defined] # noqa: S606 — fixed program, arguments validated by the caller
|
|
1247
1251
|
append_process_audit_event("setup_wizard_open_url", plan=plan, status="spawned")
|
|
1248
1252
|
else:
|
|
1249
1253
|
command = ["xdg-open", url]
|
|
@@ -1260,5 +1264,5 @@ def open_url(url: str) -> None:
|
|
|
1260
1264
|
error=str(exc),
|
|
1261
1265
|
)
|
|
1262
1266
|
except Exception:
|
|
1263
|
-
|
|
1267
|
+
quiet()
|
|
1264
1268
|
pass
|
|
@@ -152,13 +152,13 @@ DOCUMENT_MAX_READ_BYTES = 10_000_000 # 10 MB
|
|
|
152
152
|
|
|
153
153
|
|
|
154
154
|
# ── focused tool submodules (re-exported flat for import compatibility) ───────
|
|
155
|
+
from latticeai.tools.commands import * # noqa: E402,F401,F403
|
|
155
156
|
from latticeai.tools.computer import * # noqa: E402,F401,F403
|
|
156
|
-
from latticeai.tools.filesystem import * # noqa: E402,F401,F403
|
|
157
157
|
from latticeai.tools.documents import * # noqa: E402,F401,F403
|
|
158
|
-
from latticeai.tools.
|
|
158
|
+
from latticeai.tools.filesystem import * # noqa: E402,F401,F403
|
|
159
159
|
from latticeai.tools.knowledge import * # noqa: E402,F401,F403
|
|
160
|
+
from latticeai.tools.local_files import * # noqa: E402,F401,F403
|
|
160
161
|
from latticeai.tools.network import * # noqa: E402,F401,F403
|
|
161
|
-
from latticeai.tools.commands import * # noqa: E402,F401,F403
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
# ── tool registry: the single name → invocation source of truth ───────────────
|
|
@@ -11,19 +11,19 @@ from typing import Any, Dict, List, Optional
|
|
|
11
11
|
|
|
12
12
|
import latticeai.tools as tools
|
|
13
13
|
from latticeai.tools import (
|
|
14
|
-
ToolError,
|
|
15
|
-
ensure_agent_root,
|
|
16
|
-
_resolve_path,
|
|
17
|
-
_relative,
|
|
18
14
|
ALLOWED_COMMANDS,
|
|
15
|
+
ALLOWED_GIT_SUBCOMMANDS,
|
|
19
16
|
BLOCKED_COMMANDS,
|
|
20
17
|
BUILD_SCRIPT_NAMES,
|
|
21
18
|
DEPLOY_SCRIPT_NAMES,
|
|
22
|
-
ALLOWED_GIT_SUBCOMMANDS,
|
|
23
|
-
MAX_COMMAND_SECONDS,
|
|
24
19
|
MAX_BUILD_SECONDS,
|
|
25
|
-
MAX_DEPLOY_SECONDS,
|
|
26
20
|
MAX_COMMAND_OUTPUT,
|
|
21
|
+
MAX_COMMAND_SECONDS,
|
|
22
|
+
MAX_DEPLOY_SECONDS,
|
|
23
|
+
ToolError,
|
|
24
|
+
_relative,
|
|
25
|
+
_resolve_path,
|
|
26
|
+
ensure_agent_root,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
# find(1) flags that execute or delete; checked in run_command.
|