ltcai 8.9.0 → 9.1.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 +81 -58
- package/auto_setup.py +7 -904
- package/desktop/electron/README.md +9 -0
- package/desktop/electron/main.cjs +5 -3
- package/docs/CHANGELOG.md +221 -238
- package/docs/COMMUNITY_AND_PLUGINS.md +3 -2
- package/docs/DEVELOPMENT.md +53 -14
- package/docs/LEGACY_COMPATIBILITY.md +4 -4
- package/docs/ONBOARDING.md +10 -2
- package/docs/OPERATIONS.md +8 -4
- package/docs/PRODUCT_DIRECTION_REVIEW.md +4 -0
- package/docs/ROADMAP_RECOMMENDATIONS.md +61 -36
- package/docs/TRUST_MODEL.md +5 -2
- package/docs/WHY_LATTICE.md +15 -10
- package/docs/WORKFLOW_DESIGNER.md +22 -0
- package/docs/kg-schema.md +13 -2
- package/docs/mcp-tools.md +17 -6
- package/docs/privacy.md +19 -3
- package/docs/public-deploy.md +32 -3
- package/docs/security-model.md +46 -11
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/archive.py +4 -14
- package/lattice_brain/context.py +66 -9
- package/lattice_brain/embeddings.py +38 -2
- package/lattice_brain/graph/_kg_common.py +27 -462
- package/lattice_brain/graph/_kg_constants.py +243 -0
- package/lattice_brain/graph/_kg_fsutil.py +293 -0
- package/lattice_brain/graph/discovery.py +0 -948
- package/lattice_brain/graph/discovery_index.py +1126 -0
- package/lattice_brain/graph/documents.py +44 -9
- package/lattice_brain/graph/ingest.py +47 -20
- package/lattice_brain/graph/provenance.py +13 -6
- package/lattice_brain/graph/retrieval.py +141 -610
- package/lattice_brain/graph/retrieval_docgen.py +243 -0
- package/lattice_brain/graph/retrieval_vector.py +460 -0
- package/lattice_brain/graph/store.py +6 -0
- package/lattice_brain/ingestion.py +69 -4
- package/lattice_brain/portability.py +29 -23
- package/lattice_brain/quality.py +98 -4
- package/lattice_brain/runtime/agent_runtime.py +169 -7
- package/lattice_brain/runtime/hooks.py +30 -13
- package/lattice_brain/runtime/multi_agent.py +27 -8
- package/lattice_brain/runtime/statuses.py +10 -0
- package/lattice_brain/utils.py +46 -0
- package/lattice_brain/workflow.py +1 -5
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +1 -1
- package/latticeai/api/agent_registry.py +10 -8
- package/latticeai/api/agents.py +22 -3
- package/latticeai/api/auth.py +78 -16
- package/latticeai/api/browser.py +303 -34
- package/latticeai/api/chat.py +355 -952
- package/latticeai/api/chat_agent_http.py +356 -0
- package/latticeai/api/chat_contracts.py +54 -0
- package/latticeai/api/chat_documents.py +256 -0
- package/latticeai/api/chat_helpers.py +250 -0
- package/latticeai/api/chat_history.py +99 -0
- package/latticeai/api/chat_intents.py +302 -0
- package/latticeai/api/chat_stream.py +115 -0
- package/latticeai/api/computer_use.py +211 -40
- package/latticeai/api/health.py +9 -3
- package/latticeai/api/hooks.py +17 -6
- package/latticeai/api/knowledge_graph.py +78 -14
- package/latticeai/api/local_files.py +7 -2
- package/latticeai/api/marketplace.py +11 -0
- package/latticeai/api/mcp.py +102 -23
- package/latticeai/api/models.py +72 -33
- package/latticeai/api/network.py +5 -5
- package/latticeai/api/permissions.py +70 -29
- package/latticeai/api/plugins.py +21 -7
- package/latticeai/api/portability.py +5 -5
- package/latticeai/api/realtime.py +33 -5
- package/latticeai/api/setup.py +2 -2
- package/latticeai/api/static_routes.py +123 -24
- package/latticeai/api/tools.py +97 -14
- package/latticeai/api/workflow_designer.py +37 -0
- package/latticeai/api/workspace.py +2 -1
- package/latticeai/app_factory.py +185 -405
- package/latticeai/core/agent.py +19 -9
- package/latticeai/core/agent_registry.py +1 -5
- package/latticeai/core/config.py +23 -3
- package/latticeai/core/context_builder.py +21 -3
- package/latticeai/core/invitations.py +1 -4
- package/latticeai/core/io_utils.py +45 -0
- package/latticeai/core/legacy_compatibility.py +24 -3
- package/latticeai/core/local_embeddings.py +2 -4
- package/latticeai/core/marketplace.py +33 -2
- package/latticeai/core/mcp_catalog.py +450 -0
- package/latticeai/core/mcp_registry.py +2 -441
- package/latticeai/core/plugins.py +15 -0
- package/latticeai/core/policy.py +1 -1
- package/latticeai/core/realtime.py +38 -15
- package/latticeai/core/sessions.py +7 -2
- package/latticeai/core/timeutil.py +10 -0
- package/latticeai/core/tool_registry.py +90 -18
- package/latticeai/core/users.py +5 -14
- package/latticeai/core/workspace_graph_trace.py +31 -5
- package/latticeai/core/workspace_memory.py +3 -1
- package/latticeai/core/workspace_os.py +18 -7
- package/latticeai/core/workspace_os_utils.py +2 -21
- package/latticeai/core/workspace_permissions.py +2 -1
- package/latticeai/core/workspace_plugins.py +1 -1
- package/latticeai/core/workspace_runs.py +14 -5
- package/latticeai/core/workspace_skills.py +1 -1
- package/latticeai/core/workspace_snapshots.py +2 -1
- package/latticeai/core/workspace_timeline.py +2 -1
- package/latticeai/integrations/telegram_bot.py +96 -40
- package/latticeai/models/model_providers.py +111 -0
- package/latticeai/models/router.py +189 -173
- package/latticeai/runtime/access_runtime.py +62 -4
- package/latticeai/runtime/audit_runtime.py +27 -16
- package/latticeai/runtime/automation_runtime.py +22 -7
- package/latticeai/runtime/brain_runtime.py +19 -7
- package/latticeai/runtime/chat_wiring.py +2 -0
- package/latticeai/runtime/config_runtime.py +58 -26
- package/latticeai/runtime/context_runtime.py +16 -4
- package/latticeai/runtime/history_runtime.py +163 -0
- package/latticeai/runtime/hooks_runtime.py +1 -1
- package/latticeai/runtime/model_wiring.py +33 -5
- package/latticeai/runtime/namespace_runtime.py +163 -0
- package/latticeai/runtime/network_config_runtime.py +56 -0
- package/latticeai/runtime/platform_runtime_wiring.py +4 -3
- package/latticeai/runtime/review_wiring.py +1 -1
- package/latticeai/runtime/router_registration.py +34 -1
- package/latticeai/runtime/security_runtime.py +110 -13
- package/latticeai/runtime/sso_config_runtime.py +128 -0
- package/latticeai/runtime/stages.py +27 -0
- package/latticeai/runtime/user_key_runtime.py +106 -0
- package/latticeai/server_app.py +5 -1
- package/latticeai/services/architecture_readiness.py +74 -5
- package/latticeai/services/brain_automation.py +3 -26
- package/latticeai/services/chat_service.py +205 -15
- package/latticeai/services/local_knowledge.py +423 -0
- package/latticeai/services/memory_service.py +268 -21
- package/latticeai/services/model_engines.py +48 -33
- package/latticeai/services/model_errors.py +17 -0
- package/latticeai/services/model_loading.py +28 -25
- package/latticeai/services/model_runtime.py +228 -162
- package/latticeai/services/p_reinforce.py +22 -3
- package/latticeai/services/platform_runtime.py +92 -24
- package/latticeai/services/product_readiness.py +19 -17
- package/latticeai/services/review_queue.py +76 -11
- package/latticeai/services/router_context.py +1 -0
- package/latticeai/services/run_executor.py +25 -9
- package/latticeai/services/search_service.py +203 -28
- package/latticeai/services/setup_detection.py +80 -0
- package/latticeai/services/tool_dispatch.py +28 -5
- package/latticeai/services/triggers.py +53 -4
- package/latticeai/services/upload_service.py +13 -2
- package/latticeai/setup/__init__.py +25 -0
- package/latticeai/setup/auto_setup.py +857 -0
- package/latticeai/setup/wizard.py +1264 -0
- package/latticeai/tools/__init__.py +278 -0
- package/{tools → latticeai/tools}/commands.py +67 -7
- package/{tools → latticeai/tools}/computer.py +1 -1
- package/{tools → latticeai/tools}/documents.py +1 -1
- package/{tools → latticeai/tools}/filesystem.py +3 -3
- package/latticeai/tools/knowledge.py +178 -0
- package/{tools → latticeai/tools}/local_files.py +7 -1
- package/{tools → latticeai/tools}/network.py +0 -1
- package/local_knowledge_api.py +4 -342
- package/package.json +22 -2
- package/scripts/brain_quality_eval.py +3 -1
- package/scripts/bump_version.py +3 -0
- package/scripts/capture_release_evidence.mjs +180 -0
- package/scripts/check_current_release_docs.mjs +142 -0
- package/scripts/check_i18n_literals.mjs +107 -31
- package/scripts/check_openapi_drift.mjs +56 -0
- package/scripts/export_openapi.py +67 -7
- package/scripts/i18n_literal_allowlist.json +22 -24
- package/scripts/run_integration_tests.mjs +72 -10
- package/scripts/validate_release_artifacts.py +49 -7
- package/scripts/wheel_smoke.py +5 -0
- package/setup_wizard.py +3 -1304
- 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 +11 -11
- package/static/app/assets/Act-Bzz0bUyW.js +1 -0
- package/static/app/assets/Brain-Dj2J20YA.js +321 -0
- package/static/app/assets/Capture-CqlEl1Ga.js +1 -0
- package/static/app/assets/Library-B03FP1Yx.js +1 -0
- package/static/app/assets/System-80lHW0Ux.js +1 -0
- package/static/app/assets/index-BiMofBTM.js +17 -0
- package/static/app/assets/index-Bmx9rzTc.css +2 -0
- package/static/app/assets/primitives-Q1A96_7v.js +1 -0
- package/static/app/assets/textarea-D13RtnTo.js +1 -0
- package/static/app/index.html +2 -2
- package/static/css/tokens.css +4 -2
- package/static/sw.js +1 -1
- package/tools/__init__.py +21 -269
- package/docs/CODE_REVIEW_2026-07-06.md +0 -764
- package/latticeai/runtime/app_context_runtime.py +0 -13
- package/latticeai/runtime/sso_runtime.py +0 -52
- package/latticeai/runtime/tail_wiring.py +0 -21
- package/scripts/com.pts.claudecode.discord.plist +0 -31
- package/scripts/pts-claudecode-discord-bridge.mjs +0 -207
- package/scripts/start-pts-claudecode-discord.sh +0 -51
- package/static/app/assets/Act-fZokUnC0.js +0 -1
- package/static/app/assets/Brain-DtyuWubr.js +0 -321
- package/static/app/assets/Capture-D5KV3Cu7.js +0 -1
- package/static/app/assets/Library-C9kyFkSt.js +0 -1
- package/static/app/assets/System-VbChmX7r.js +0 -1
- package/static/app/assets/index-DCh5AoXt.css +0 -2
- package/static/app/assets/index-DPdcPoF0.js +0 -17
- package/static/app/assets/primitives-DFeanEV6.js +0 -1
- package/static/app/assets/textarea-CD8UNKIy.js +0 -1
- package/tools/knowledge.py +0 -95
package/latticeai/core/agent.py
CHANGED
|
@@ -30,7 +30,8 @@ from typing import Any, Awaitable, Callable, Dict, FrozenSet, List, Optional
|
|
|
30
30
|
|
|
31
31
|
from lattice_brain.runtime.hooks import dispatch_tool
|
|
32
32
|
from lattice_brain.runtime.contracts import runtime_boundary_contract, single_agent_contract
|
|
33
|
-
from
|
|
33
|
+
from latticeai.core.tool_registry import SCOPED_KNOWLEDGE_TOOLS
|
|
34
|
+
from latticeai.tools import ToolError
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class AgentState(str, Enum):
|
|
@@ -155,7 +156,7 @@ class SingleAgentRuntime:
|
|
|
155
156
|
entrypoint="latticeai.core.agent.SingleAgentRuntime",
|
|
156
157
|
surface="/agent",
|
|
157
158
|
owns="single-agent PLAN / EXECUTE / VERIFY state machine over injected ports",
|
|
158
|
-
compatibility_aliases=[
|
|
159
|
+
compatibility_aliases=[],
|
|
159
160
|
)
|
|
160
161
|
|
|
161
162
|
def config(self) -> Dict[str, Any]:
|
|
@@ -254,12 +255,20 @@ class SingleAgentRuntime:
|
|
|
254
255
|
+ "\n".join(f"- {c}" for c in ctx.corrections)
|
|
255
256
|
) if ctx.corrections else ""
|
|
256
257
|
|
|
258
|
+
request_workspace = getattr(req, "workspace_id", None)
|
|
259
|
+
recent_kwargs = {
|
|
260
|
+
"conversation_id": req.conversation_id,
|
|
261
|
+
"user_email": current_user or None,
|
|
262
|
+
}
|
|
263
|
+
if request_workspace is not None:
|
|
264
|
+
recent_kwargs["workspace_id"] = request_workspace
|
|
265
|
+
recent_conversation = d.recent_chat_context(**recent_kwargs) or "(none)"
|
|
257
266
|
context = (
|
|
258
267
|
f"{d.executor_prompt}\n\n"
|
|
259
268
|
f"[LANGUAGE HINT: {lang_hint}]\n"
|
|
260
269
|
f"Workspace root: {d.agent_root}\n\n"
|
|
261
270
|
f"PLAN:\n{json.dumps(ctx.plan, ensure_ascii=False)}\n\n"
|
|
262
|
-
f"Recent conversation:\n{
|
|
271
|
+
f"Recent conversation:\n{recent_conversation}\n\n"
|
|
263
272
|
f"User request: {req.message}{corrections_hint}\n\n"
|
|
264
273
|
f"Execution transcript:\n{json.dumps(ctx.transcript, ensure_ascii=False, indent=2)}"
|
|
265
274
|
)
|
|
@@ -281,6 +290,13 @@ class SingleAgentRuntime:
|
|
|
281
290
|
thoughts = str(action.get("thoughts") or "")[:600]
|
|
282
291
|
args = action.get("args") or {}
|
|
283
292
|
|
|
293
|
+
if name in SCOPED_KNOWLEDGE_TOOLS:
|
|
294
|
+
# Scope is server-owned, never model-owned. Overwrite any
|
|
295
|
+
# claimed values before policy evaluation, audit, and dispatch.
|
|
296
|
+
args = dict(args)
|
|
297
|
+
args["workspace_id"] = request_workspace or "personal"
|
|
298
|
+
args["user_email"] = current_user or "local"
|
|
299
|
+
|
|
284
300
|
if name == "final":
|
|
285
301
|
ctx.final_message = action.get("message", "작업을 완료했습니다.")
|
|
286
302
|
ctx.transcript.append({
|
|
@@ -530,9 +546,3 @@ class SingleAgentRuntime:
|
|
|
530
546
|
ctx.state = AgentState.FAILED
|
|
531
547
|
|
|
532
548
|
ctx.state_history.append(ctx.state.value)
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
# Backward compatibility: external callers historically imported
|
|
536
|
-
# ``latticeai.core.agent.AgentRuntime`` for the single-agent state machine.
|
|
537
|
-
# The product/runtime facade lives at ``lattice_brain.runtime.agent_runtime``.
|
|
538
|
-
AgentRuntime = SingleAgentRuntime
|
|
@@ -18,7 +18,6 @@ from __future__ import annotations
|
|
|
18
18
|
import json
|
|
19
19
|
import os
|
|
20
20
|
import tempfile
|
|
21
|
-
from datetime import datetime
|
|
22
21
|
from pathlib import Path
|
|
23
22
|
from typing import Any, Dict, List, Optional
|
|
24
23
|
|
|
@@ -28,14 +27,11 @@ from lattice_brain.runtime.multi_agent import (
|
|
|
28
27
|
MULTI_AGENT_VERSION,
|
|
29
28
|
ROLE_AGENT_IDS,
|
|
30
29
|
)
|
|
30
|
+
from .timeutil import now_iso as _now
|
|
31
31
|
|
|
32
32
|
AGENT_TYPES = ("planner", "researcher", "executor", "reviewer", "release", "custom")
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def _now() -> str:
|
|
36
|
-
return datetime.now().isoformat(timespec="seconds")
|
|
37
|
-
|
|
38
|
-
|
|
39
35
|
# Capabilities + descriptions for the built-in role agents. Kept here as the
|
|
40
36
|
# registry's metadata projection of the roles defined in multi_agent.py.
|
|
41
37
|
ROLE_META: Dict[str, Dict[str, Any]] = {
|
package/latticeai/core/config.py
CHANGED
|
@@ -99,6 +99,7 @@ class Config:
|
|
|
99
99
|
rate_limit_enabled: bool
|
|
100
100
|
open_registration: bool
|
|
101
101
|
invite_code: str
|
|
102
|
+
invite_cookie_secret: str
|
|
102
103
|
invite_gate_enabled: bool
|
|
103
104
|
admin_emails: List[str]
|
|
104
105
|
trusted_proxies: List[str]
|
|
@@ -160,6 +161,7 @@ class Config:
|
|
|
160
161
|
host = _value(env, "LATTICEAI_HOST", "127.0.0.1")
|
|
161
162
|
port = _port(env, "LATTICEAI_PORT", 4825)
|
|
162
163
|
network_exposed = not host_is_loopback(host)
|
|
164
|
+
externally_reachable = is_public or network_exposed
|
|
163
165
|
|
|
164
166
|
cors_extra = [item.strip() for item in _value(env, "LATTICEAI_CORS_ALLOWED_ORIGINS", "").split(",") if item.strip()]
|
|
165
167
|
admin_emails = [item.strip().lower() for item in _value(env, "LATTICEAI_ADMIN_EMAILS", "").split(",") if item.strip()]
|
|
@@ -189,13 +191,31 @@ class Config:
|
|
|
189
191
|
autoload_models=_bool(env, "LATTICEAI_AUTOLOAD_MODELS", default=is_public),
|
|
190
192
|
model_idle_unload_seconds=_int(env, "LATTICEAI_MODEL_IDLE_UNLOAD_SECONDS", 0),
|
|
191
193
|
allow_local_models=_bool(env, "LATTICEAI_ALLOW_LOCAL_MODELS", default=not is_public),
|
|
192
|
-
|
|
194
|
+
# Authentication is optional only for the local-first loopback
|
|
195
|
+
# profile. An explicit ``false`` must never turn a public/LAN
|
|
196
|
+
# binding into an unauthenticated service.
|
|
197
|
+
require_auth=(
|
|
198
|
+
True
|
|
199
|
+
if externally_reachable
|
|
200
|
+
else _bool(env, "LATTICEAI_REQUIRE_AUTH", default=False)
|
|
201
|
+
),
|
|
193
202
|
allow_plaintext_api_keys=_bool(env, "LATTICEAI_ALLOW_PLAINTEXT_API_KEYS", default=False),
|
|
194
203
|
cors_allow_network=_bool(env, "LATTICEAI_CORS_ALLOW_NETWORK", default=False),
|
|
195
204
|
cors_extra_origins=cors_extra,
|
|
196
205
|
rate_limit_enabled=_str(env, "LATTICEAI_RATE_LIMIT", "1") != "0",
|
|
197
|
-
|
|
198
|
-
|
|
206
|
+
# Public/LAN startup is closed-registration even if a stale or
|
|
207
|
+
# unsafe environment file attempts to opt back in.
|
|
208
|
+
open_registration=(
|
|
209
|
+
False
|
|
210
|
+
if externally_reachable
|
|
211
|
+
else _bool(env, "LATTICEAI_OPEN_REGISTRATION", default=True)
|
|
212
|
+
),
|
|
213
|
+
# There is deliberately no repository-wide/default invitation
|
|
214
|
+
# code. When the gate is enabled, the security runtime persists a
|
|
215
|
+
# cryptographically random code if the operator did not provide
|
|
216
|
+
# one explicitly.
|
|
217
|
+
invite_code=_value(env, "LATTICEAI_INVITE_CODE", ""),
|
|
218
|
+
invite_cookie_secret=_value(env, "LATTICEAI_INVITE_COOKIE_SECRET", ""),
|
|
199
219
|
invite_gate_enabled=_bool(env, "LATTICEAI_INVITE_GATE_ENABLED", default=False),
|
|
200
220
|
admin_emails=admin_emails,
|
|
201
221
|
trusted_proxies=trusted_proxies,
|
|
@@ -23,6 +23,8 @@ def retrieve_context_for_generation(
|
|
|
23
23
|
*,
|
|
24
24
|
max_results: int = 10,
|
|
25
25
|
max_hops: int = 2,
|
|
26
|
+
allowed_workspaces=None,
|
|
27
|
+
include_legacy_global: bool = False,
|
|
26
28
|
) -> Dict[str, Any]:
|
|
27
29
|
"""Knowledge Graph에서 문서 생성에 필요한 컨텍스트를 검색·조합한다.
|
|
28
30
|
|
|
@@ -38,9 +40,21 @@ def retrieve_context_for_generation(
|
|
|
38
40
|
if not query or not kg_store:
|
|
39
41
|
return {"query": query, "context_markdown": "", "sources": [], "stats": {}}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
scope_kwargs = (
|
|
44
|
+
{
|
|
45
|
+
"allowed_workspaces": allowed_workspaces,
|
|
46
|
+
"include_legacy_global": include_legacy_global,
|
|
47
|
+
}
|
|
48
|
+
if allowed_workspaces is not None
|
|
49
|
+
else {}
|
|
50
|
+
)
|
|
51
|
+
results = kg_store.search_for_document_generation(query, limit=max_results, **scope_kwargs)
|
|
42
52
|
if not results:
|
|
43
|
-
fallback_ctx = kg_store.context_for_query(
|
|
53
|
+
fallback_ctx = kg_store.context_for_query(
|
|
54
|
+
query,
|
|
55
|
+
limit=max_results,
|
|
56
|
+
**scope_kwargs,
|
|
57
|
+
)
|
|
44
58
|
return {
|
|
45
59
|
"query": query,
|
|
46
60
|
"context_markdown": fallback_ctx,
|
|
@@ -49,7 +63,11 @@ def retrieve_context_for_generation(
|
|
|
49
63
|
}
|
|
50
64
|
|
|
51
65
|
seed_ids = [r["id"] for r in results]
|
|
52
|
-
hop_data = kg_store.multi_hop_context(
|
|
66
|
+
hop_data = kg_store.multi_hop_context(
|
|
67
|
+
seed_ids,
|
|
68
|
+
max_hops=max_hops,
|
|
69
|
+
**scope_kwargs,
|
|
70
|
+
)
|
|
53
71
|
|
|
54
72
|
extra_nodes_by_id = {}
|
|
55
73
|
for node in hop_data.get("nodes", []):
|
|
@@ -9,10 +9,7 @@ from datetime import datetime, timedelta
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any, Dict, List, Optional
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
def _now() -> datetime:
|
|
14
|
-
return datetime.now()
|
|
15
|
-
|
|
12
|
+
from .timeutil import local_now as _now
|
|
16
13
|
|
|
17
14
|
def _iso(dt: datetime) -> str:
|
|
18
15
|
return dt.isoformat(timespec="seconds")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Shared small IO helpers for JSON, timestamps, and file hashes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def atomic_write_json(path: Path, payload: Dict[str, Any]) -> None:
|
|
14
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
tmp_path = path.with_suffix(path.suffix + ".tmp")
|
|
16
|
+
fd = os.open(tmp_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
17
|
+
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
18
|
+
handle.write(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
19
|
+
os.replace(tmp_path, path)
|
|
20
|
+
try:
|
|
21
|
+
path.chmod(0o600)
|
|
22
|
+
except OSError:
|
|
23
|
+
# Windows and unusual filesystems may not expose POSIX mode bits; the
|
|
24
|
+
# atomic write is still the safest supported fallback there.
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def parse_iso(value: Optional[str]) -> Optional[datetime]:
|
|
29
|
+
if not value:
|
|
30
|
+
return None
|
|
31
|
+
try:
|
|
32
|
+
return datetime.fromisoformat(str(value))
|
|
33
|
+
except (TypeError, ValueError):
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def sha256_file(path: Path) -> str:
|
|
38
|
+
digest = hashlib.sha256()
|
|
39
|
+
with open(path, "rb") as fh:
|
|
40
|
+
for block in iter(lambda: fh.read(65536), b""):
|
|
41
|
+
digest.update(block)
|
|
42
|
+
return digest.hexdigest()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = ["atomic_write_json", "parse_iso", "sha256_file"]
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any, Dict, List
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
LEGACY_COMPATIBILITY_VERSION = "
|
|
17
|
+
LEGACY_COMPATIBILITY_VERSION = "9.1.0"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclass(frozen=True)
|
|
@@ -89,13 +89,34 @@ LEGACY_SHIMS: List[LegacyShim] = [
|
|
|
89
89
|
reason="Deployment docs and local launch scripts historically targeted server.py.",
|
|
90
90
|
removal_phase="major-release-after-8.x",
|
|
91
91
|
),
|
|
92
|
+
LegacyShim(
|
|
93
|
+
path="tools/",
|
|
94
|
+
owner="latticeai.tools",
|
|
95
|
+
replacement="from latticeai.tools import execute_tool",
|
|
96
|
+
reason="Existing integrations and tests import the historical root tools package.",
|
|
97
|
+
removal_phase="major-release-after-9.x",
|
|
98
|
+
),
|
|
92
99
|
LegacyShim(
|
|
93
100
|
path="local_knowledge_api.py",
|
|
94
|
-
owner="latticeai.
|
|
95
|
-
replacement="from latticeai.
|
|
101
|
+
owner="latticeai.services.local_knowledge",
|
|
102
|
+
replacement="from latticeai.services.local_knowledge import create_local_knowledge_router",
|
|
96
103
|
reason="Local folder ingestion integrations used the root local knowledge API.",
|
|
97
104
|
removal_phase="requires-api-route-migration",
|
|
98
105
|
),
|
|
106
|
+
LegacyShim(
|
|
107
|
+
path="auto_setup.py",
|
|
108
|
+
owner="latticeai.setup.auto_setup",
|
|
109
|
+
replacement="from latticeai.setup.auto_setup import probe, recommend",
|
|
110
|
+
reason="Historical scripts invoke the zero-config setup module from the repo root.",
|
|
111
|
+
removal_phase="major-release-after-9.x",
|
|
112
|
+
),
|
|
113
|
+
LegacyShim(
|
|
114
|
+
path="setup_wizard.py",
|
|
115
|
+
owner="latticeai.setup.wizard",
|
|
116
|
+
replacement="from latticeai.setup.wizard import scan_environment",
|
|
117
|
+
reason="Older integrations imported the setup wizard from the repo root.",
|
|
118
|
+
removal_phase="major-release-after-9.x",
|
|
119
|
+
),
|
|
99
120
|
]
|
|
100
121
|
|
|
101
122
|
# Shim layers that have completed their compatibility window and were
|
|
@@ -11,15 +11,13 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import hashlib
|
|
13
13
|
import math
|
|
14
|
+
import os
|
|
14
15
|
import re
|
|
15
16
|
import struct
|
|
16
17
|
from dataclasses import dataclass
|
|
17
18
|
from typing import Iterable, List
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
# Removed direct os.getenv per 7.6.0 config centralization (review.md item 3).
|
|
21
|
-
# Callers that need override should pass dim= from Config.
|
|
22
|
-
DEFAULT_EMBEDDING_DIM = 384
|
|
20
|
+
DEFAULT_EMBEDDING_DIM = int(os.getenv("LATTICEAI_VECTOR_DIM", "384"))
|
|
23
21
|
EMBEDDING_MODEL_ID = f"lattice-local-hash-v1:{DEFAULT_EMBEDDING_DIM}"
|
|
24
22
|
|
|
25
23
|
|
|
@@ -11,8 +11,8 @@ from copy import deepcopy
|
|
|
11
11
|
from typing import Any, Dict, List, Optional
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
MARKETPLACE_VERSION = "
|
|
15
|
-
TEMPLATE_KINDS = ("plugin", "workflow", "agent")
|
|
14
|
+
MARKETPLACE_VERSION = "9.1.0"
|
|
15
|
+
TEMPLATE_KINDS = ("plugin", "workflow", "agent", "ingestion_bridge")
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def _agent_template(
|
|
@@ -144,6 +144,37 @@ BUILTIN_TEMPLATES: Dict[str, List[Dict[str, Any]]] = {
|
|
|
144
144
|
category="automation",
|
|
145
145
|
),
|
|
146
146
|
],
|
|
147
|
+
"ingestion_bridge": [
|
|
148
|
+
{
|
|
149
|
+
"id": "bridge-obsidian-markdown",
|
|
150
|
+
"kind": "ingestion_bridge",
|
|
151
|
+
"name": "Obsidian Markdown Bridge",
|
|
152
|
+
"version": "1.0.0",
|
|
153
|
+
"description": "Import a local Markdown vault through the unified ingestion pipeline.",
|
|
154
|
+
"metadata": {"category": "interop", "installable": True},
|
|
155
|
+
"definition": {
|
|
156
|
+
"source_types": ["local_file", "markdown"],
|
|
157
|
+
"file_patterns": ["*.md"],
|
|
158
|
+
"pipeline": "unified-ingestion",
|
|
159
|
+
"provenance": True,
|
|
160
|
+
"graph_edges": ["indexed_from", "mentions"],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"id": "bridge-calendar-notes",
|
|
165
|
+
"kind": "ingestion_bridge",
|
|
166
|
+
"name": "Calendar Notes Bridge",
|
|
167
|
+
"version": "1.0.0",
|
|
168
|
+
"description": "Normalize calendar meeting notes into Brain events and source-linked notes.",
|
|
169
|
+
"metadata": {"category": "interop", "installable": True},
|
|
170
|
+
"definition": {
|
|
171
|
+
"source_types": ["workspace_event", "note"],
|
|
172
|
+
"pipeline": "unified-ingestion",
|
|
173
|
+
"provenance": True,
|
|
174
|
+
"graph_edges": ["indexed_from", "mentions"],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
147
178
|
}
|
|
148
179
|
|
|
149
180
|
|