ltcai 9.0.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 +80 -46
- package/auto_setup.py +7 -853
- package/desktop/electron/README.md +9 -0
- package/desktop/electron/main.cjs +5 -3
- package/docs/CHANGELOG.md +146 -2
- 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/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 +1 -6
- package/lattice_brain/context.py +66 -9
- package/lattice_brain/graph/_kg_fsutil.py +1 -5
- package/lattice_brain/graph/discovery_index.py +174 -20
- 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 +140 -39
- package/lattice_brain/graph/retrieval_docgen.py +37 -4
- package/lattice_brain/ingestion.py +4 -7
- package/lattice_brain/portability.py +28 -14
- package/lattice_brain/runtime/agent_runtime.py +5 -9
- 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 +20 -2
- 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 +320 -850
- 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 +24 -1
- 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 +62 -9
- 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/mcp.py +102 -23
- package/latticeai/api/models.py +72 -33
- package/latticeai/api/network.py +5 -5
- package/latticeai/api/permissions.py +67 -26
- 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 +96 -14
- package/latticeai/api/workflow_designer.py +37 -0
- package/latticeai/api/workspace.py +2 -1
- package/latticeai/app_factory.py +110 -52
- 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 +9 -1
- package/latticeai/core/legacy_compatibility.py +24 -3
- package/latticeai/core/marketplace.py +1 -1
- 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 +1 -5
- 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 +0 -5
- 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 +94 -43
- package/latticeai/models/router.py +130 -36
- package/latticeai/runtime/access_runtime.py +62 -4
- package/latticeai/runtime/automation_runtime.py +13 -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/hooks_runtime.py +1 -1
- package/latticeai/runtime/model_wiring.py +33 -5
- package/latticeai/runtime/namespace_runtime.py +62 -72
- 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/stages.py +27 -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 +55 -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 +83 -23
- package/latticeai/services/product_readiness.py +19 -17
- package/latticeai/services/review_queue.py +12 -0
- package/latticeai/services/router_context.py +1 -0
- package/latticeai/services/run_executor.py +4 -9
- package/latticeai/services/search_service.py +203 -28
- 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 +1 -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 -1260
- 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/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/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-21lIXx2E.js +0 -1
- package/static/app/assets/Brain-BqUd5UJJ.js +0 -321
- package/static/app/assets/Capture-BA7Z2Q1u.js +0 -1
- package/static/app/assets/Library-bFMtyni3.js +0 -1
- package/static/app/assets/System-K6krGCqn.js +0 -1
- package/static/app/assets/index-C4R3ws30.js +0 -17
- package/static/app/assets/index-ChSeOB02.css +0 -2
- package/static/app/assets/primitives-sQU3it5I.js +0 -1
- package/static/app/assets/textarea-DK3Fd_lR.js +0 -1
- package/tools/knowledge.py +0 -95
|
@@ -20,7 +20,9 @@ from fastapi import HTTPException, Request
|
|
|
20
20
|
from lattice_brain.runtime.hooks import dispatch_tool
|
|
21
21
|
from lattice_brain.runtime.multi_agent import MultiAgentOrchestrator, default_role_runner, llm_role_runner
|
|
22
22
|
from lattice_brain.workflow import ApprovalRequired, WorkflowEngine
|
|
23
|
-
from
|
|
23
|
+
from latticeai.core.tool_registry import SCOPED_KNOWLEDGE_TOOLS
|
|
24
|
+
from latticeai.services.tool_dispatch import enforce_tool_policy
|
|
25
|
+
from latticeai.tools import execute_tool
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class PlatformRuntime:
|
|
@@ -38,6 +40,7 @@ class PlatformRuntime:
|
|
|
38
40
|
llm_generate: Optional[Callable[..., str]] = None,
|
|
39
41
|
llm_available: Optional[Callable[[], bool]] = None,
|
|
40
42
|
agent_registry: Any = None,
|
|
43
|
+
memory_recall: Optional[Callable[..., Dict[str, Any]]] = None,
|
|
41
44
|
):
|
|
42
45
|
self.store = store
|
|
43
46
|
self.svc = workspace_service
|
|
@@ -55,6 +58,7 @@ class PlatformRuntime:
|
|
|
55
58
|
self.llm_generate = llm_generate
|
|
56
59
|
self.llm_available = llm_available or (lambda: False)
|
|
57
60
|
self.agent_registry = agent_registry
|
|
61
|
+
self.memory_recall = memory_recall
|
|
58
62
|
|
|
59
63
|
# ── request gating ────────────────────────────────────────────────────
|
|
60
64
|
|
|
@@ -75,7 +79,12 @@ class PlatformRuntime:
|
|
|
75
79
|
workspaces = self.svc.list_workspaces(user or None).get("workspaces", [])
|
|
76
80
|
return {ws.get("workspace_id") for ws in workspaces if ws.get("workspace_id")}
|
|
77
81
|
except Exception:
|
|
78
|
-
|
|
82
|
+
# ``None`` deliberately means "unscoped local/no-auth mode" to
|
|
83
|
+
# search and realtime consumers. Returning it for an authenticated
|
|
84
|
+
# account on a storage/service failure would therefore fail open
|
|
85
|
+
# and expose every workspace. Authenticated failures see nothing;
|
|
86
|
+
# only the explicit anonymous local mode keeps legacy semantics.
|
|
87
|
+
return set() if user else None
|
|
79
88
|
|
|
80
89
|
# ── plugin lifecycle hooks ────────────────────────────────────────────
|
|
81
90
|
|
|
@@ -86,7 +95,7 @@ class PlatformRuntime:
|
|
|
86
95
|
|
|
87
96
|
# ── shared node runners ───────────────────────────────────────────────
|
|
88
97
|
|
|
89
|
-
def _tool_node_runner(self):
|
|
98
|
+
def _tool_node_runner(self, user=None, scope=None):
|
|
90
99
|
"""Workflow tool node: EXECUTES the tool under governance (v4).
|
|
91
100
|
|
|
92
101
|
Auto-approve tools run immediately through the shared dispatch_tool
|
|
@@ -99,9 +108,12 @@ class PlatformRuntime:
|
|
|
99
108
|
def runner(*, node, context):
|
|
100
109
|
cfg = node.get("config") or {}
|
|
101
110
|
name = cfg.get("tool") or ""
|
|
102
|
-
args = cfg.get("args") or {}
|
|
111
|
+
args = dict(cfg.get("args") or {})
|
|
103
112
|
if not name:
|
|
104
113
|
raise ValueError("tool node has no tool configured")
|
|
114
|
+
if name in SCOPED_KNOWLEDGE_TOOLS:
|
|
115
|
+
args["workspace_id"] = scope or "personal"
|
|
116
|
+
args["user_email"] = user or "local"
|
|
105
117
|
try:
|
|
106
118
|
permission = dict(self.get_tool_permission(name, args))
|
|
107
119
|
except TypeError:
|
|
@@ -142,6 +154,31 @@ class PlatformRuntime:
|
|
|
142
154
|
|
|
143
155
|
def _context_provider(self, user, scope):
|
|
144
156
|
def provider(goal: str):
|
|
157
|
+
recall = getattr(self, "memory_recall", None)
|
|
158
|
+
if recall is not None:
|
|
159
|
+
try:
|
|
160
|
+
payload = recall(
|
|
161
|
+
goal,
|
|
162
|
+
user_email=user,
|
|
163
|
+
workspace_id=scope,
|
|
164
|
+
limit=8,
|
|
165
|
+
)
|
|
166
|
+
results = payload.get("results") or payload.get("items") or []
|
|
167
|
+
evidence = []
|
|
168
|
+
for item in results[:8]:
|
|
169
|
+
if not isinstance(item, dict):
|
|
170
|
+
continue
|
|
171
|
+
source = str(item.get("source") or "memory")
|
|
172
|
+
title = str(item.get("title") or item.get("kind") or "Memory")
|
|
173
|
+
snippet = str(item.get("snippet") or item.get("content") or "").strip()
|
|
174
|
+
if snippet:
|
|
175
|
+
evidence.append(f"[{source}] {title}: {snippet[:240]}")
|
|
176
|
+
if evidence:
|
|
177
|
+
return evidence
|
|
178
|
+
except Exception:
|
|
179
|
+
# Recall is an enrichment seam; the legacy store fallback
|
|
180
|
+
# keeps agent execution available if it degrades.
|
|
181
|
+
pass
|
|
145
182
|
try:
|
|
146
183
|
mems = self.store.search_memories(goal, user_email=user, workspace_id=scope).get("memories", [])
|
|
147
184
|
ctx = [str(m.get("content") or "")[:180] for m in mems[:6]]
|
|
@@ -152,6 +189,14 @@ class PlatformRuntime:
|
|
|
152
189
|
ctx = synth + ctx
|
|
153
190
|
except Exception:
|
|
154
191
|
pass
|
|
192
|
+
if not ctx:
|
|
193
|
+
try:
|
|
194
|
+
recent = self.store.list_memories(user_email=user, workspace_id=scope).get("memories", [])
|
|
195
|
+
# list_memories is newest-first; automation should ground
|
|
196
|
+
# itself in the knowledge that just entered the Brain.
|
|
197
|
+
ctx = [str(m.get("content") or "")[:180] for m in recent[:8]]
|
|
198
|
+
except Exception:
|
|
199
|
+
pass
|
|
155
200
|
return ctx[:8]
|
|
156
201
|
except Exception:
|
|
157
202
|
return []
|
|
@@ -169,15 +214,20 @@ class PlatformRuntime:
|
|
|
169
214
|
tool = args.get("tool") or (manifest.provides.get("tools") or [None])[0]
|
|
170
215
|
if not tool:
|
|
171
216
|
raise ValueError(f"plugin '{plugin_id}' run_tool needs a tool name")
|
|
172
|
-
|
|
173
|
-
if
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
217
|
+
args = dict(args)
|
|
218
|
+
if tool in SCOPED_KNOWLEDGE_TOOLS:
|
|
219
|
+
args["workspace_id"] = scope or "personal"
|
|
220
|
+
args["user_email"] = user or "local"
|
|
221
|
+
policy = enforce_tool_policy(
|
|
222
|
+
tool,
|
|
223
|
+
args,
|
|
224
|
+
current_user=user or "",
|
|
225
|
+
source="plugin",
|
|
226
|
+
)
|
|
227
|
+
permission = dict(self.get_tool_permission(tool, args))
|
|
178
228
|
result = dispatch_tool(self.hooks, tool, args, lambda: execute_tool(tool, args),
|
|
179
229
|
source=f"plugin:{plugin_id}")
|
|
180
|
-
return {"plugin": plugin_id, "tool": tool, "permission": permission,
|
|
230
|
+
return {"plugin": plugin_id, "tool": tool, "permission": permission, "policy": dict(policy),
|
|
181
231
|
"executed": True, "result": result}
|
|
182
232
|
|
|
183
233
|
def run_workflow(*, plugin_id, action, args, manifest):
|
|
@@ -206,8 +256,18 @@ class PlatformRuntime:
|
|
|
206
256
|
def _agent_node_runner(self, user, scope):
|
|
207
257
|
def runner(*, node, context):
|
|
208
258
|
cfg = node.get("config") or {}
|
|
209
|
-
goal = cfg.get("goal") or context.get("goal") or "Run agent"
|
|
210
|
-
|
|
259
|
+
goal = cfg.get("goal") or cfg.get("prompt") or context.get("goal") or "Run agent"
|
|
260
|
+
roles = cfg.get("roles")
|
|
261
|
+
if not roles and (cfg.get("mode") == "draft" or cfg.get("prompt")):
|
|
262
|
+
roles = ["researcher", "planner", "executor", "reviewer"]
|
|
263
|
+
return self.run_agent(
|
|
264
|
+
goal,
|
|
265
|
+
user,
|
|
266
|
+
scope,
|
|
267
|
+
with_workflow=False,
|
|
268
|
+
roles=roles,
|
|
269
|
+
inputs=context.get("inputs"),
|
|
270
|
+
)
|
|
211
271
|
return runner
|
|
212
272
|
|
|
213
273
|
# ── cross-system runs ─────────────────────────────────────────────────
|
|
@@ -218,7 +278,7 @@ class PlatformRuntime:
|
|
|
218
278
|
except FileNotFoundError:
|
|
219
279
|
return {"error": f"workflow not found: {workflow_id}"}
|
|
220
280
|
runners = {
|
|
221
|
-
"tool": self._tool_node_runner(),
|
|
281
|
+
"tool": self._tool_node_runner(user, scope),
|
|
222
282
|
"skill": self._skill_node_runner(),
|
|
223
283
|
"plugin": self._plugin_node_runner(user, scope),
|
|
224
284
|
}
|
|
@@ -236,12 +296,8 @@ class PlatformRuntime:
|
|
|
236
296
|
return {"workflow_run_id": run["id"], "status": result.status}
|
|
237
297
|
|
|
238
298
|
def run_agent(self, goal, user, scope, *, with_workflow: bool, roles=None, inputs=None) -> Dict[str, Any]:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
plugin_runner=lambda pid, ctx: self.registry.execute_action(pid, "run_skill", {}, runners=self.plugin_capability_runners(user, scope), workspace_id=scope).as_dict(),
|
|
242
|
-
context_provider=self._context_provider(user, scope),
|
|
243
|
-
)
|
|
244
|
-
result = MultiAgentOrchestrator(role_runner=role_runner).run(
|
|
299
|
+
orchestrator = self.build_orchestrator(user, scope, with_workflow=with_workflow)
|
|
300
|
+
result = orchestrator.run(
|
|
245
301
|
goal, user_email=user, workspace_id=scope, roles=roles, inputs=inputs or {}
|
|
246
302
|
)
|
|
247
303
|
run = self.store.record_agent_run(
|
|
@@ -259,14 +315,18 @@ class PlatformRuntime:
|
|
|
259
315
|
|
|
260
316
|
def build_workflow_runners(self, user, scope) -> Dict[str, Callable[..., Any]]:
|
|
261
317
|
return {
|
|
262
|
-
"tool": self._tool_node_runner(),
|
|
318
|
+
"tool": self._tool_node_runner(user, scope),
|
|
263
319
|
"skill": self._skill_node_runner(),
|
|
264
320
|
"plugin": self._plugin_node_runner(user, scope),
|
|
265
321
|
"agent": self._agent_node_runner(user, scope),
|
|
266
322
|
}
|
|
267
323
|
|
|
268
|
-
def build_orchestrator(self, user, scope) -> MultiAgentOrchestrator:
|
|
269
|
-
workflow_runner =
|
|
324
|
+
def build_orchestrator(self, user, scope, *, with_workflow: bool = True) -> MultiAgentOrchestrator:
|
|
325
|
+
workflow_runner = (
|
|
326
|
+
(lambda wf_ref, ctx: self.run_workflow_by_id(wf_ref, user, scope, with_agent=False, inputs=ctx.inputs))
|
|
327
|
+
if with_workflow
|
|
328
|
+
else None
|
|
329
|
+
)
|
|
270
330
|
plugin_runner = lambda pid, ctx: self.registry.execute_action(pid, "run_skill", {}, runners=self.plugin_capability_runners(user, scope), workspace_id=scope).as_dict() # noqa: E731
|
|
271
331
|
context_provider = self._context_provider(user, scope)
|
|
272
332
|
custom_agents = {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Machine-checkable
|
|
1
|
+
"""Machine-checkable product readiness gates for the current release.
|
|
2
2
|
|
|
3
3
|
Where ``architecture_readiness`` proves the internal structure is sound, this
|
|
4
4
|
module answers the product question the 8.4 release exists to settle: *does the
|
|
@@ -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.
|
|
21
|
+
PRODUCT_VERSION_TARGET = "9.1.0"
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@dataclass(frozen=True)
|
|
@@ -37,11 +37,12 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
37
37
|
evidence=[
|
|
38
38
|
"docs/ONBOARDING.md::five-minute",
|
|
39
39
|
"frontend/src/components/ProductFlow.tsx::WakeBrainScreen",
|
|
40
|
-
"frontend/src/features/brain/BrainConversation.tsx::
|
|
40
|
+
"frontend/src/features/brain/BrainConversation.tsx::brain-home-insights",
|
|
41
41
|
"frontend/src/features/brain/BrainConversation.tsx::BrainBriefPanel",
|
|
42
42
|
"frontend/src/features/brain/BrainHome.tsx",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
43
|
+
"frontend/src/App.tsx::brain-mobile-nav",
|
|
44
|
+
"latticeai/setup/auto_setup.py",
|
|
45
|
+
"latticeai/setup/wizard.py",
|
|
45
46
|
],
|
|
46
47
|
),
|
|
47
48
|
ProductGate(
|
|
@@ -57,7 +58,8 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
57
58
|
title="File action requests create artifacts instead of code-only answers",
|
|
58
59
|
evidence=[
|
|
59
60
|
"latticeai/api/chat.py::is_file_action_request",
|
|
60
|
-
"latticeai/api/
|
|
61
|
+
"latticeai/api/chat_intents.py::direct_file_action",
|
|
62
|
+
"latticeai/api/chat_intents.py::direct_write_file",
|
|
61
63
|
"tests/unit/test_chat_telegram_decoupling.py::test_chat_file_creation_intent_writes_real_file",
|
|
62
64
|
],
|
|
63
65
|
),
|
|
@@ -76,10 +78,10 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
76
78
|
evidence=[
|
|
77
79
|
"package.json::release:artifacts",
|
|
78
80
|
"package.json::release:validate",
|
|
79
|
-
"README.md::dist/ltcai-
|
|
80
|
-
"README.md::dist/ltcai-
|
|
81
|
-
"README.md::dist/ltcai-
|
|
82
|
-
"README.md::ltcai-
|
|
81
|
+
f"README.md::dist/ltcai-{PRODUCT_VERSION_TARGET}-py3-none-any.whl",
|
|
82
|
+
f"README.md::dist/ltcai-{PRODUCT_VERSION_TARGET}.tar.gz",
|
|
83
|
+
f"README.md::dist/ltcai-{PRODUCT_VERSION_TARGET}.vsix",
|
|
84
|
+
f"README.md::ltcai-{PRODUCT_VERSION_TARGET}.tgz",
|
|
83
85
|
"scripts/validate_release_artifacts.py",
|
|
84
86
|
"scripts/release_smoke.py",
|
|
85
87
|
"Dockerfile",
|
|
@@ -95,14 +97,14 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
95
97
|
title="Release story is documented and honest",
|
|
96
98
|
evidence=[
|
|
97
99
|
"README.md",
|
|
98
|
-
"README.md::The current release is **
|
|
99
|
-
"SECURITY.md::
|
|
100
|
-
"vscode-extension/README.md::**
|
|
101
|
-
"docs/CHANGELOG.md::## [
|
|
100
|
+
f"README.md::The current release is **{PRODUCT_VERSION_TARGET}",
|
|
101
|
+
f"SECURITY.md::{'.'.join(PRODUCT_VERSION_TARGET.split('.')[:2])}.x (latest)",
|
|
102
|
+
f"vscode-extension/README.md::**{PRODUCT_VERSION_TARGET}",
|
|
103
|
+
f"docs/CHANGELOG.md::## [{PRODUCT_VERSION_TARGET}]",
|
|
102
104
|
"FEATURE_STATUS.md",
|
|
103
|
-
"
|
|
105
|
+
f"RELEASE_NOTES_v{PRODUCT_VERSION_TARGET}.md",
|
|
104
106
|
"latticeai/core/agent.py::SingleAgentRuntime",
|
|
105
|
-
"
|
|
107
|
+
"lattice_brain/runtime/agent_runtime.py::class AgentRuntime",
|
|
106
108
|
"lattice_brain/runtime/contracts.py::runtime-boundary/v1",
|
|
107
109
|
"lattice_brain/runtime/contracts.py::RuntimeBoundaryProtocol",
|
|
108
110
|
"lattice_brain/runtime/agent_runtime.py::def boundary",
|
|
@@ -115,7 +117,7 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
115
117
|
id="ecosystem-path",
|
|
116
118
|
title="Community and plugin growth path is explicit",
|
|
117
119
|
evidence=[
|
|
118
|
-
"docs/COMMUNITY_AND_PLUGINS.md::
|
|
120
|
+
f"docs/COMMUNITY_AND_PLUGINS.md::{PRODUCT_VERSION_TARGET}",
|
|
119
121
|
"docs/PLUGIN_SDK.md",
|
|
120
122
|
"plugins/README.md",
|
|
121
123
|
"plugins/hello-world/plugin.json",
|
|
@@ -49,6 +49,14 @@ def review_queue_opted_in(workflow: Dict[str, Any]) -> bool:
|
|
|
49
49
|
continue
|
|
50
50
|
if (node.get("config") or {}).get("review_queue") is True:
|
|
51
51
|
return True
|
|
52
|
+
metadata = workflow.get("metadata") or {}
|
|
53
|
+
# Compatibility for recipe drafts installed before review_queue became an
|
|
54
|
+
# explicit trigger flag. They are local-only and already consent-gated.
|
|
55
|
+
if (
|
|
56
|
+
metadata.get("created_from") == "brain_automation_recipe"
|
|
57
|
+
and metadata.get("external_actions") is False
|
|
58
|
+
):
|
|
59
|
+
return True
|
|
52
60
|
return False
|
|
53
61
|
|
|
54
62
|
|
|
@@ -307,6 +315,10 @@ def _extract_run_id(result: Any) -> Optional[str]:
|
|
|
307
315
|
if isinstance(result, dict):
|
|
308
316
|
if result.get("run_id"):
|
|
309
317
|
return str(result["run_id"])
|
|
318
|
+
if result.get("workflow_run_id"):
|
|
319
|
+
return str(result["workflow_run_id"])
|
|
320
|
+
if result.get("agent_run_id"):
|
|
321
|
+
return str(result["agent_run_id"])
|
|
310
322
|
run = result.get("run")
|
|
311
323
|
if isinstance(run, dict) and run.get("id"):
|
|
312
324
|
return str(run["id"])
|
|
@@ -34,6 +34,7 @@ class ToolRouterContext:
|
|
|
34
34
|
install_mcp: Any
|
|
35
35
|
mcp_public_item: Any
|
|
36
36
|
hooks: Any = None
|
|
37
|
+
workspace_service: Any = None
|
|
37
38
|
# Resolves a caller email to their allowed workspace set (None = no scoping,
|
|
38
39
|
# i.e. single-user / no-auth mode). Threaded to the knowledge-graph router so
|
|
39
40
|
# its read endpoints enforce the same workspace boundary as /api/search.
|
|
@@ -10,18 +10,13 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import asyncio
|
|
12
12
|
from dataclasses import dataclass
|
|
13
|
-
from datetime import datetime
|
|
14
13
|
from typing import Any, Callable, Dict, Optional
|
|
15
14
|
|
|
15
|
+
from lattice_brain.runtime.statuses import (
|
|
16
|
+
RUN_ACTIVE_STATUSES as ACTIVE_STATUSES,
|
|
17
|
+
)
|
|
16
18
|
from lattice_brain.workflow import WorkflowEngine
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ACTIVE_STATUSES = {"queued", "running", "in_progress", "retrying", "cancelling"}
|
|
20
|
-
TERMINAL_STATUSES = {"ok", "retried_ok", "failed", "rejected", "cancelled", "interrupted", "partial"}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _now() -> str:
|
|
24
|
-
return datetime.now().isoformat(timespec="seconds")
|
|
19
|
+
from latticeai.core.timeutil import now_iso as _now
|
|
25
20
|
|
|
26
21
|
|
|
27
22
|
@dataclass
|