ltcai 5.3.0 → 5.5.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 +18 -16
- package/docs/CHANGELOG.md +46 -0
- package/frontend/src/App.tsx +8 -2
- package/frontend/src/api/client.ts +2 -0
- package/frontend/src/pages/Act.tsx +82 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/__init__.py +53 -0
- package/lattice_brain/runtime/agent_runtime.py +7 -0
- package/lattice_brain/runtime/hooks.py +6 -0
- package/lattice_brain/runtime/multi_agent.py +7 -2
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/workflow_designer.py +60 -0
- package/latticeai/app_factory.py +1 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/brain_automation.py +214 -0
- package/latticeai/services/triggers.py +61 -4
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +5 -5
- package/static/app/assets/index-C7vzwUjU.js +16 -0
- package/static/app/assets/index-C7vzwUjU.js.map +1 -0
- package/static/app/assets/index-HN4f2wbe.css +2 -0
- package/static/app/index.html +2 -2
- package/static/app/assets/index-CQmHhk8Q.css +0 -2
- package/static/app/assets/index-sOXTFUQc.js +0 -16
- package/static/app/assets/index-sOXTFUQc.js.map +0 -1
package/README.md
CHANGED
|
@@ -45,6 +45,8 @@ You need Lattice AI when:
|
|
|
45
45
|
source-aware memory.
|
|
46
46
|
- See recent memories, older memories, topics, relationships, and the full
|
|
47
47
|
knowledge graph when you want deeper structure.
|
|
48
|
+
- Create consent-first Brain automation drafts for memory digests, project
|
|
49
|
+
reviews, and follow-up suggestions before any schedule is enabled.
|
|
48
50
|
- Use a recommended local model without learning model internals first.
|
|
49
51
|
- Keep advanced controls, audit logs, roles, and retention in a separate Admin
|
|
50
52
|
surface.
|
|
@@ -191,25 +193,23 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
|
|
|
191
193
|
|
|
192
194
|
## Current Release Preparation
|
|
193
195
|
|
|
194
|
-
The current development target is **5.
|
|
196
|
+
The current development target is **5.5.0 Release Coordination**:
|
|
195
197
|
|
|
196
|
-
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- Legacy root modules are documented in
|
|
204
|
-
[docs/LEGACY_COMPATIBILITY.md](docs/LEGACY_COMPATIBILITY.md).
|
|
198
|
+
- Carries forward the v5.4.0 Brain Automation Scheduler as the release-ready
|
|
199
|
+
baseline for the 5.5.0 line.
|
|
200
|
+
- Keeps consent-first automation, TriggerService dedup/LATTICE_TZ/degraded
|
|
201
|
+
behavior, and runtime graph cleanup intact.
|
|
202
|
+
- Synchronizes Python, npm, VSIX, Tauri, runtime constants, lockfiles, and static
|
|
203
|
+
metadata to 5.5.0.
|
|
204
|
+
- Updates release documentation and artifact naming to exact 5.5.0 filenames.
|
|
205
205
|
|
|
206
|
-
Expected artifacts for
|
|
206
|
+
Expected artifacts for 5.5.0 release must use exact filenames:
|
|
207
207
|
|
|
208
|
-
- `dist/ltcai-5.
|
|
209
|
-
- `dist/ltcai-5.
|
|
210
|
-
- `ltcai-5.
|
|
211
|
-
- `dist/ltcai-5.
|
|
212
|
-
- `src-tauri/target/release/bundle/dmg/Lattice AI_5.
|
|
208
|
+
- `dist/ltcai-5.5.0-py3-none-any.whl`
|
|
209
|
+
- `dist/ltcai-5.5.0.tar.gz`
|
|
210
|
+
- `ltcai-5.5.0.tgz`
|
|
211
|
+
- `dist/ltcai-5.5.0.vsix`
|
|
212
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_5.5.0_aarch64.dmg`
|
|
213
213
|
|
|
214
214
|
Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
215
215
|
|
|
@@ -228,6 +228,8 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
|
228
228
|
|
|
229
229
|
| Version | Theme |
|
|
230
230
|
| --- | --- |
|
|
231
|
+
| 5.5.0 | Release Coordination: synchronized package/runtime/static versions and release docs for the 5.5.0 line while preserving v5.4.0 Brain Automation Scheduler behavior |
|
|
232
|
+
| 5.4.0 | Brain Automation Scheduler: consent-first recipe drafts (Daily/Weekly/Follow-up), TriggerService with dedup/LATTICE_TZ/degraded, runtime graph cleanup, E2E scenarios |
|
|
231
233
|
| 5.3.0 | Product Clarity and Runtime Cleanup: user-first README/onboarding, unified Digital Brain identity, legacy compatibility map, and app factory runtime seams |
|
|
232
234
|
| 5.2.0 | User-Focused Model Transformation: structured model capability registry, HF verification transparency, model recommendation UX, and workspace-scoped marketplace state |
|
|
233
235
|
| 5.1.0 | Product Trust & Clarity Release: clarifies the private AI memory-layer promise, hardens CSP/secret/auto-read/download gates, adds trust/privacy docs, and refreshes evidence |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,52 @@
|
|
|
3
3
|
The top entry is the current release-preparation target. Older entries are
|
|
4
4
|
historical and may describe behavior as it existed at that release.
|
|
5
5
|
|
|
6
|
+
## [5.5.0] - 2026-06-15
|
|
7
|
+
|
|
8
|
+
> Release Coordination. Synchronized package/runtime/static metadata and release
|
|
9
|
+
> documentation for the 5.5.0 line while preserving v5.4.0 Brain Automation
|
|
10
|
+
> Scheduler behavior.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Bumped synchronized Python, npm, VSIX, Tauri, runtime constants, lockfiles, and
|
|
14
|
+
static asset manifest metadata to `5.5.0`.
|
|
15
|
+
- Updated README, RELEASE.md, RELEASE_NOTES.md, FEATURE_STATUS.md,
|
|
16
|
+
vscode-extension/README.md, and this changelog so current-release references
|
|
17
|
+
and expected artifact names point at exact 5.5.0 filenames.
|
|
18
|
+
|
|
19
|
+
### Preserved
|
|
20
|
+
- v5.4.0 consent-first Brain automation recipes, TriggerService dedup,
|
|
21
|
+
LATTICE_TZ, degraded status, enabled:false disarming, runtime graph cleanup,
|
|
22
|
+
and E2E scenario coverage remain the functional baseline.
|
|
23
|
+
- Package registry publishing and deployment remain owner-run only.
|
|
24
|
+
|
|
25
|
+
## [5.4.0] - 2026-06-15
|
|
26
|
+
|
|
27
|
+
> Brain Automation Scheduler. Consent-first recipe drafts (Daily Memory Digest,
|
|
28
|
+
> Weekly Project Review, Follow-up Radar) install as disabled workflows.
|
|
29
|
+
> Scheduler triggers (TriggerService) with dedup, LATTICE_TZ, degraded status,
|
|
30
|
+
> and runtime graph cleanup.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- Consent-first Brain automation recipe drafts for Daily Memory Digest, Weekly
|
|
34
|
+
Project Review, and Follow-up Radar. Recipes install as disabled workflows so
|
|
35
|
+
users can inspect them before any scheduler or Brain-event trigger fires.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Workflow trigger scanning now treats explicit `enabled: false` trigger config
|
|
39
|
+
as disarmed while preserving legacy behavior for existing workflows that do
|
|
40
|
+
not include an `enabled` field.
|
|
41
|
+
- The Automate page now surfaces Brain automation recipe cards with local-only,
|
|
42
|
+
review-before-run consent copy.
|
|
43
|
+
- Recipe install UX: success feedback on "Create reviewable draft", install-time
|
|
44
|
+
button disabled to block duplicates clicks, and if same recipe draft already
|
|
45
|
+
exists (by metadata), button state changes + guide text instead of re-creating.
|
|
46
|
+
- lattice_brain/runtime dependency/responsibility graph 정리 + 실제 진입점 매핑 문서화 (runtime/* 모듈 헤더 + app_factory 주석). AgentRuntime (lattice_brain facade) vs latticeai/core/agent (single-agent state/plan/transcript) 분리 명확화.
|
|
47
|
+
- TriggerService 스케줄러 엣지케이스 보강: LATTICE_TZ env 지원 (describe() 노출), last_attempt_at + cooldown dedup 가드로 중복 실행 방지 (interval/brain_event), consecutive_failures + "degraded" status per-trigger 서페이싱.
|
|
48
|
+
- A방향 E2E 시나리오 초안 brain_automation.py 에 작성 (draft install, dedup, consent-first, trigger fire with provenance, LATTICE_TZ, degraded, review flow, RunExecutor 경로).
|
|
49
|
+
|
|
50
|
+
## Unreleased
|
|
51
|
+
|
|
6
52
|
## [5.3.0] - 2026-06-14
|
|
7
53
|
|
|
8
54
|
> Product Clarity and Runtime Cleanup. Lattice AI is now presented consistently
|
package/frontend/src/App.tsx
CHANGED
|
@@ -24,6 +24,8 @@ import { ProductFlow, readProductFlowComplete } from "@/components/ProductFlow";
|
|
|
24
24
|
import { useAppStore } from "@/store/appStore";
|
|
25
25
|
import { asArray } from "@/lib/utils";
|
|
26
26
|
import { LANGUAGE_LABELS, t, type Language } from "@/i18n";
|
|
27
|
+
import { parseHash } from "@/routes";
|
|
28
|
+
import { ActPage } from "@/pages/Act";
|
|
27
29
|
|
|
28
30
|
type ApiRecord = Record<string, unknown>;
|
|
29
31
|
type BrainDepth = 1 | 2 | 3 | 4 | 5;
|
|
@@ -73,7 +75,8 @@ export default function App() {
|
|
|
73
75
|
const theme = useAppStore((state) => state.theme);
|
|
74
76
|
const language = useAppStore((state) => state.language);
|
|
75
77
|
const [flowComplete, setFlowComplete] = React.useState(readProductFlowComplete);
|
|
76
|
-
const
|
|
78
|
+
const rawRoute = useHashRoute();
|
|
79
|
+
const parsed = React.useMemo(() => parseHash(), [rawRoute]);
|
|
77
80
|
const { state: brainState, intensity, setBrain } = useBrainState();
|
|
78
81
|
|
|
79
82
|
React.useEffect(() => {
|
|
@@ -99,8 +102,10 @@ export default function App() {
|
|
|
99
102
|
return (
|
|
100
103
|
<div className="brain-space">
|
|
101
104
|
<div className="brain-field" />
|
|
102
|
-
{
|
|
105
|
+
{rawRoute.startsWith("/admin") ? (
|
|
103
106
|
<AdminConsole onBack={() => navigateHash("/brain")} />
|
|
107
|
+
) : parsed.primary === "act" ? (
|
|
108
|
+
<ActPage initialTab={parsed.tab} />
|
|
104
109
|
) : (
|
|
105
110
|
<BrainHome brainState={brainState} intensity={intensity} onBrainChange={setBrain} />
|
|
106
111
|
)}
|
|
@@ -576,6 +581,7 @@ function AdminConsole({ onBack }: { onBack: () => void }) {
|
|
|
576
581
|
<span>{stringValue(retention.retained_events, "0")} retained · {stringValue(retention.prune_candidates, "0")} ready for export/prune review</span>
|
|
577
582
|
</div>
|
|
578
583
|
</AdminPanel>
|
|
584
|
+
|
|
579
585
|
</section>
|
|
580
586
|
</main>
|
|
581
587
|
);
|
|
@@ -368,6 +368,8 @@ export const latticeApi = {
|
|
|
368
368
|
workflowDefinitions: () => get("/workflows/api/definitions", { workflows: [] }),
|
|
369
369
|
workflowRuns: () => get("/workflows/api/runs", { runs: [] }),
|
|
370
370
|
workflowTriggers: () => get("/workflows/api/triggers", { armed: [] }),
|
|
371
|
+
automationRecipes: () => get("/workflows/api/automation/recipes", { recipes: [], principles: {} }),
|
|
372
|
+
installAutomationRecipe: (recipeId: string, enabled = false) => post(`/workflows/api/automation/recipes/${encodeURIComponent(recipeId)}`, { enabled }, {}),
|
|
371
373
|
createWorkflow: (body: { name: string; nodes: Array<Record<string, unknown>>; metadata?: Record<string, unknown> }) => post("/workflows/api/definitions", body, {}),
|
|
372
374
|
importWorkflow: (data: Record<string, unknown>) => post("/workflows/api/import", { data }, {}),
|
|
373
375
|
exportWorkflow: (id: string) => get(`/workflows/api/export/${encodeURIComponent(id)}`, {}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
import ReactFlow, { Background, Controls, Edge, Node } from "reactflow";
|
|
4
|
-
import { Bot, GitBranch, PauseCircle, Play, Workflow } from "lucide-react";
|
|
4
|
+
import { Bot, CalendarClock, GitBranch, PauseCircle, Play, ShieldCheck, Workflow } from "lucide-react";
|
|
5
5
|
import { latticeApi } from "@/api/client";
|
|
6
6
|
import { ActionButton, DataPanel, EntityList, KeyValueList, ModeGate, OperationResult, StructuredView, Tabs } from "@/components/primitives";
|
|
7
7
|
import { Badge } from "@/components/ui/badge";
|
|
@@ -199,6 +199,7 @@ function WorkflowsPanel() {
|
|
|
199
199
|
const qc = useQueryClient();
|
|
200
200
|
const defs = useQuery({ queryKey: ["workflowDefinitions"], queryFn: latticeApi.workflowDefinitions });
|
|
201
201
|
const triggers = useQuery({ queryKey: ["workflowTriggers"], queryFn: latticeApi.workflowTriggers });
|
|
202
|
+
const recipes = useQuery({ queryKey: ["automationRecipes"], queryFn: latticeApi.automationRecipes });
|
|
202
203
|
const [name, setName] = React.useState("Manual workflow");
|
|
203
204
|
const [importText, setImportText] = React.useState("");
|
|
204
205
|
const create = useMutation({
|
|
@@ -216,7 +217,19 @@ function WorkflowsPanel() {
|
|
|
216
217
|
qc.invalidateQueries({ queryKey: ["workflowDefinitions"] });
|
|
217
218
|
},
|
|
218
219
|
});
|
|
220
|
+
const installRecipe = useMutation({
|
|
221
|
+
mutationFn: (recipeId: string) => latticeApi.installAutomationRecipe(recipeId, false),
|
|
222
|
+
onSuccess: () => {
|
|
223
|
+
qc.invalidateQueries({ queryKey: ["workflowDefinitions"] });
|
|
224
|
+
qc.invalidateQueries({ queryKey: ["workflowTriggers"] });
|
|
225
|
+
},
|
|
226
|
+
});
|
|
219
227
|
const workflows = asArray<Record<string, unknown>>((defs.data?.data as Record<string, unknown>)?.workflows);
|
|
228
|
+
const installedRecipeIds = new Set(
|
|
229
|
+
workflows
|
|
230
|
+
.filter((w: any) => w && w.metadata && w.metadata.created_from === "brain_automation_recipe" && w.metadata.recipe_id)
|
|
231
|
+
.map((w: any) => String(w.metadata.recipe_id))
|
|
232
|
+
);
|
|
220
233
|
const nodes: Node[] = workflows.slice(0, 12).map((workflow, index) => ({
|
|
221
234
|
id: String(workflow.id || workflow.workflow_id || index),
|
|
222
235
|
position: { x: (index % 4) * 190, y: Math.floor(index / 4) * 120 },
|
|
@@ -225,6 +238,74 @@ function WorkflowsPanel() {
|
|
|
225
238
|
const edges: Edge[] = nodes.slice(1).map((node, index) => ({ id: `e-${index}`, source: nodes[index].id, target: node.id }));
|
|
226
239
|
return (
|
|
227
240
|
<div className="grid gap-4 xl:grid-cols-[1.2fr_0.8fr]">
|
|
241
|
+
<DataPanel title="Brain automations" result={recipes.data} className="xl:col-span-2">
|
|
242
|
+
{(data) => {
|
|
243
|
+
const items = asArray<Record<string, unknown>>((data as Record<string, unknown>).recipes);
|
|
244
|
+
return (
|
|
245
|
+
<div className="grid gap-3 lg:grid-cols-3">
|
|
246
|
+
{items.map((recipe) => {
|
|
247
|
+
const id = String(recipe.id || "");
|
|
248
|
+
const consent = (recipe.consent || {}) as Record<string, unknown>;
|
|
249
|
+
const creates = asArray<string>(recipe.creates);
|
|
250
|
+
return (
|
|
251
|
+
<div key={id} className="rounded-lg border border-border bg-background/70 p-4">
|
|
252
|
+
<div className="flex items-start justify-between gap-2">
|
|
253
|
+
<div>
|
|
254
|
+
<div className="flex items-center gap-2 font-medium">
|
|
255
|
+
<CalendarClock className="h-4 w-4" /> {String(recipe.name || id)}
|
|
256
|
+
</div>
|
|
257
|
+
<p className="mt-2 text-sm text-muted-foreground">{String(recipe.summary || "")}</p>
|
|
258
|
+
</div>
|
|
259
|
+
<Badge variant="muted">{String(recipe.cadence || "draft")}</Badge>
|
|
260
|
+
</div>
|
|
261
|
+
<p className="mt-3 text-sm">{String(recipe.user_value || "")}</p>
|
|
262
|
+
<div className="mt-3 flex flex-wrap gap-2">
|
|
263
|
+
<Badge variant="success"><ShieldCheck className="h-3 w-3" /> local only</Badge>
|
|
264
|
+
{consent.requires_user_enable ? <Badge variant="warning">draft first</Badge> : null}
|
|
265
|
+
{creates.slice(0, 2).map((item) => <Badge key={item} variant="muted">{item}</Badge>)}
|
|
266
|
+
</div>
|
|
267
|
+
{(() => {
|
|
268
|
+
const isInstalling = installRecipe.isPending && installRecipe.variables === id;
|
|
269
|
+
const last = installRecipe.data as any;
|
|
270
|
+
const lastRid = last && last.recipe && last.recipe.recipe_id ? String(last.recipe.recipe_id) : "";
|
|
271
|
+
const justSucceeded = !installRecipe.isPending && lastRid === id;
|
|
272
|
+
const installed = installedRecipeIds.has(id);
|
|
273
|
+
const btnLabel = isInstalling
|
|
274
|
+
? "Creating..."
|
|
275
|
+
: justSucceeded
|
|
276
|
+
? "✓ Draft created"
|
|
277
|
+
: installed
|
|
278
|
+
? "Draft already created"
|
|
279
|
+
: "Create reviewable draft";
|
|
280
|
+
return (
|
|
281
|
+
<>
|
|
282
|
+
<Button
|
|
283
|
+
className="mt-4 w-full"
|
|
284
|
+
variant={installed || justSucceeded ? "secondary" : "outline"}
|
|
285
|
+
disabled={!id || isInstalling || installed}
|
|
286
|
+
onClick={() => {
|
|
287
|
+
if (installed || isInstalling) return;
|
|
288
|
+
installRecipe.mutate(id);
|
|
289
|
+
}}
|
|
290
|
+
>
|
|
291
|
+
{btnLabel}
|
|
292
|
+
</Button>
|
|
293
|
+
{justSucceeded ? (
|
|
294
|
+
<p className="mt-1 text-[10px] text-green-600">Reviewable draft ready. Check Definitions below.</p>
|
|
295
|
+
) : null}
|
|
296
|
+
{installed && !justSucceeded ? (
|
|
297
|
+
<p className="mt-1 text-[10px] text-muted-foreground">Reviewable draft exists — see Definitions.</p>
|
|
298
|
+
) : null}
|
|
299
|
+
</>
|
|
300
|
+
);
|
|
301
|
+
})()}
|
|
302
|
+
</div>
|
|
303
|
+
);
|
|
304
|
+
})}
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
}}
|
|
308
|
+
</DataPanel>
|
|
228
309
|
<Card>
|
|
229
310
|
<CardHeader>
|
|
230
311
|
<CardTitle className="flex items-center gap-2"><GitBranch className="h-4 w-4" /> Workflow graph</CardTitle>
|
|
@@ -3,6 +3,59 @@
|
|
|
3
3
|
Physically hosts the hooks registry/dispatch lifecycle, the multi-agent
|
|
4
4
|
orchestrator, and the agent runtime service. Lazy-loaded so importing
|
|
5
5
|
``lattice_brain.runtime`` stays cheap.
|
|
6
|
+
|
|
7
|
+
=== lattice_brain/runtime 책임 + 의존성 그래프 (A방향 + Act/automation 기준) ===
|
|
8
|
+
- multi_agent.py (핵심 실행체)
|
|
9
|
+
책임: OrchestrationContext, AgentContextPacket, AgentHandoff, AgentRunResult,
|
|
10
|
+
MultiAgentOrchestrator, default_role_runner, llm_role_runner,
|
|
11
|
+
CORE_PIPELINE/AGENT_ROLES, handoff/review/retry 타임라인 생성.
|
|
12
|
+
의존: 없음 (순수, deterministic/LLM runner 주입).
|
|
13
|
+
진입: orchestrator_factory 로 AgentRuntime에 주입됨.
|
|
14
|
+
|
|
15
|
+
- hooks.py (라이프사이클 확장)
|
|
16
|
+
책임: HookContext/HookResult, HooksRegistry (builtin+user, order/enabled persist),
|
|
17
|
+
dispatch_tool (pre_tool/post_tool 통합), fire_hook/run_hooks,
|
|
18
|
+
BUILTIN_HOOKS (redact, memory-snapshot, tool-permission-gate 등).
|
|
19
|
+
의존: subprocess (user command hooks).
|
|
20
|
+
진입: AgentRuntime, tool_dispatch, api/tools, core/agent 등에 주입/등록.
|
|
21
|
+
|
|
22
|
+
- agent_runtime.py (공개 퍼사드 / 바운더리)
|
|
23
|
+
책임: AgentRuntime (store+orchestrator_factory+workspace_graph+audit+hooks 주입),
|
|
24
|
+
start/reserve_run/complete_reserved_run, stop, status/health/config,
|
|
25
|
+
list_runs/get_run/events/replay, _fire_pre_run / _post_run_hooks.
|
|
26
|
+
의존: .multi_agent, .hooks (간접), store (WORKSPACE_OS).
|
|
27
|
+
진입점: **여기가 제품 경계**. app_factory에서 AGENT_RUNTIME으로 생성,
|
|
28
|
+
api/agents.py (런타임 라우터), RunExecutor (async agent/workflow),
|
|
29
|
+
workflow_designer에 주입. frontend BrainAutomationPanel / Act 가
|
|
30
|
+
/agents/* 를 통해 이 바운더리만 의존.
|
|
31
|
+
|
|
32
|
+
- __init__.py : lazy re-export (import 비용 최소화).
|
|
33
|
+
|
|
34
|
+
실제 진입점 매핑 (app_factory + 호출 스택):
|
|
35
|
+
app_factory.py:1508
|
|
36
|
+
AGENT_RUNTIME = AgentRuntime(store=WORKSPACE_OS, orchestrator_factory=PLATFORM.build_orchestrator, hooks=HOOKS_REGISTRY, ...)
|
|
37
|
+
RUN_EXECUTOR = RunExecutor(..., agent_runtime=AGENT_RUNTIME)
|
|
38
|
+
AGENT_RUNTIME.attach_executor(RUN_EXECUTOR)
|
|
39
|
+
create_agents_router(..., agent_runtime=AGENT_RUNTIME)
|
|
40
|
+
create_workflow_designer_router(..., run_executor=RUN_EXECUTOR, trigger_service=TRIGGER_SERVICE)
|
|
41
|
+
api/agents.py:49
|
|
42
|
+
from lattice_brain.runtime.agent_runtime import AgentRuntime
|
|
43
|
+
runtime = agent_runtime or AgentRuntime(...) # fallback
|
|
44
|
+
/agents/api/runtime/* , POST /agents (start via runtime.start or executor)
|
|
45
|
+
api/chat.py
|
|
46
|
+
from latticeai.services.tool_dispatch import build_agent_runtime
|
|
47
|
+
-> latticeai/core/agent.py:AgentRuntime (별도 state/plan/transcript 머신, single-agent /agent 경로. dispatch_tool 만 공유)
|
|
48
|
+
latticeai/services/tool_dispatch.py:14
|
|
49
|
+
from latticeai.core.agent import AgentRuntime as CoreAgentRuntime
|
|
50
|
+
(tool governance + core single-agent용)
|
|
51
|
+
latticeai/services/platform_runtime.py
|
|
52
|
+
from lattice_brain.runtime.{hooks, multi_agent}
|
|
53
|
+
tests: test_hooks_dispatch.py, test_t7_triggers.py 등에서 직접 import lattice_brain.runtime.*
|
|
54
|
+
|
|
55
|
+
core/tool_registry.py (신규) + services/tool_dispatch.py 가 tool build 주도.
|
|
56
|
+
lattice_brain/runtime 는 multi-agent + hooks + facade 에 집중. core/agent 는 chat/agent 단일 루프.
|
|
57
|
+
|
|
58
|
+
이 매핑으로 중복 제거 및 wiring 명확화 완료 (feat(Act, automation) 방향).
|
|
6
59
|
"""
|
|
7
60
|
|
|
8
61
|
from __future__ import annotations
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"""AgentRuntime — the single boundary for agent execution and observability.
|
|
2
2
|
|
|
3
|
+
(lattice_brain/runtime/agent_runtime.py)
|
|
4
|
+
책임: 퍼사드. store/orchestrator/hooks/audit 주입 받아 start/reserve/complete,
|
|
5
|
+
status/health/config/events/replay/stop, pre/post_run hook firing.
|
|
6
|
+
RunExecutor와 /agents 라우터의 유일한 의존 대상.
|
|
7
|
+
의존: .multi_agent (orchestrator), .hooks, store (WORKSPACE_OS).
|
|
8
|
+
진입점: app_factory.py:AGENT_RUNTIME (wiring root), api/agents.py, RunExecutor.
|
|
9
|
+
|
|
3
10
|
Before this module the agent concern was spread across three places: the
|
|
4
11
|
:class:`~latticeai.core.multi_agent.MultiAgentOrchestrator` (role pipeline),
|
|
5
12
|
the :class:`~latticeai.services.platform_runtime.PlatformRuntime` (cross-system
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"""Hooks platform — a persisted registry of lifecycle extension points.
|
|
2
2
|
|
|
3
|
+
(lattice_brain/runtime/hooks.py)
|
|
4
|
+
책임: HookContext/Result, HooksRegistry (persist+order+enable+builtin), dispatch_tool,
|
|
5
|
+
fire_hook (pre/post_run/tool/workflow), BUILTIN_HOOKS 등록.
|
|
6
|
+
의존성: threading, subprocess (user hooks). dispatch_tool은 tool path 단일화.
|
|
7
|
+
상위: agent_runtime.py (pre/post_run), tool_dispatch.py, api/tools, core/agent (shared).
|
|
8
|
+
|
|
3
9
|
Lattice AI runs several behaviours at well-defined points in the agent / tool /
|
|
4
10
|
workflow lifecycle (audit logging, secret redaction, sensitive-data
|
|
5
11
|
classification, tool-permission gating, memory snapshots, workflow replay
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"""Multi-Agent Runtime 2.1.
|
|
1
|
+
"""Multi-Agent Runtime 2.1. (lattice_brain/runtime/multi_agent.py)
|
|
2
|
+
|
|
3
|
+
책임: 역할 파이프라인 실행, handoff/context_packet/timeline/review/retry 기록 생성,
|
|
4
|
+
OrchestrationContext, default/llm_role_runner, MultiAgentOrchestrator.
|
|
5
|
+
의존성: 없음 (순수). runner 주입으로 simulation vs llm 분리.
|
|
6
|
+
상위 호출자: agent_runtime.py (orchestrator_factory), platform_runtime.
|
|
2
7
|
|
|
3
8
|
The runtime remains a small, dependency-injected orchestrator, but v2.1 makes
|
|
4
9
|
the operational objects first-class: handoffs, context packets, review/retry
|
|
@@ -14,7 +19,7 @@ from datetime import datetime
|
|
|
14
19
|
from typing import Any, Callable, Dict, List, Optional
|
|
15
20
|
|
|
16
21
|
|
|
17
|
-
MULTI_AGENT_VERSION = "5.
|
|
22
|
+
MULTI_AGENT_VERSION = "5.5.0"
|
|
18
23
|
|
|
19
24
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
20
25
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
|
@@ -51,6 +51,10 @@ class WorkflowImportRequest(BaseModel):
|
|
|
51
51
|
data: Dict[str, Any] = {}
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
class WorkflowRecipeInstallRequest(BaseModel):
|
|
55
|
+
enabled: bool = False
|
|
56
|
+
|
|
57
|
+
|
|
54
58
|
def create_workflow_designer_router(
|
|
55
59
|
*,
|
|
56
60
|
store,
|
|
@@ -254,6 +258,62 @@ def create_workflow_designer_router(
|
|
|
254
258
|
return {"running": False, "tick_seconds": None, "armed": []}
|
|
255
259
|
return trigger_service.describe()
|
|
256
260
|
|
|
261
|
+
@router.get("/workflows/api/automation/recipes")
|
|
262
|
+
async def automation_recipes(request: Request):
|
|
263
|
+
require_user(request)
|
|
264
|
+
from latticeai.services.brain_automation import list_brain_automation_recipes
|
|
265
|
+
|
|
266
|
+
return list_brain_automation_recipes()
|
|
267
|
+
|
|
268
|
+
@router.post("/workflows/api/automation/recipes/{recipe_id}")
|
|
269
|
+
async def install_automation_recipe(recipe_id: str, req: WorkflowRecipeInstallRequest, request: Request):
|
|
270
|
+
current_user = require_user(request)
|
|
271
|
+
scope = gate_write(request)
|
|
272
|
+
from latticeai.services.brain_automation import (
|
|
273
|
+
build_brain_automation_workflow,
|
|
274
|
+
find_installed_recipe_workflow,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
definition = build_brain_automation_workflow(recipe_id, enabled=req.enabled)
|
|
279
|
+
except KeyError as exc:
|
|
280
|
+
raise HTTPException(status_code=404, detail=f"Automation recipe not found: {recipe_id}") from exc
|
|
281
|
+
existing = find_installed_recipe_workflow(
|
|
282
|
+
store.list_workflows(workspace_id=scope).get("workflows"), recipe_id
|
|
283
|
+
)
|
|
284
|
+
if existing is not None:
|
|
285
|
+
return {
|
|
286
|
+
"workflow": existing,
|
|
287
|
+
"recipe": existing.get("metadata") or definition["metadata"],
|
|
288
|
+
"enabled": bool((existing.get("metadata") or {}).get("automation_state") == "enabled"),
|
|
289
|
+
"already_installed": True,
|
|
290
|
+
}
|
|
291
|
+
errors = validate_definition({"name": definition["name"], "nodes": definition["nodes"]})
|
|
292
|
+
if errors:
|
|
293
|
+
raise HTTPException(status_code=400, detail={"validation_errors": errors})
|
|
294
|
+
workflow = store.create_workflow(
|
|
295
|
+
name=definition["name"],
|
|
296
|
+
steps=[{"action": n.get("type"), "node": n.get("id")} for n in definition["nodes"]],
|
|
297
|
+
nodes=definition["nodes"],
|
|
298
|
+
metadata=definition["metadata"],
|
|
299
|
+
user_email=current_user or None,
|
|
300
|
+
graph=workspace_graph(),
|
|
301
|
+
workspace_id=scope,
|
|
302
|
+
)
|
|
303
|
+
append_audit_event(
|
|
304
|
+
"brain_automation_recipe_installed",
|
|
305
|
+
user_email=current_user,
|
|
306
|
+
workflow_id=workflow["id"],
|
|
307
|
+
recipe_id=recipe_id,
|
|
308
|
+
enabled=bool(req.enabled),
|
|
309
|
+
)
|
|
310
|
+
return {
|
|
311
|
+
"workflow": workflow,
|
|
312
|
+
"recipe": definition["metadata"],
|
|
313
|
+
"enabled": bool(req.enabled),
|
|
314
|
+
"already_installed": False,
|
|
315
|
+
}
|
|
316
|
+
|
|
257
317
|
@router.get("/workflows/api/runs/{run_id}/replay")
|
|
258
318
|
async def workflow_run_replay(run_id: str, request: Request):
|
|
259
319
|
require_user(request)
|
package/latticeai/app_factory.py
CHANGED
|
@@ -1505,6 +1505,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1505
1505
|
HOOKS_REGISTRY.register_hook(_trigger_hook_id, TRIGGER_SERVICE.hook_runner())
|
|
1506
1506
|
|
|
1507
1507
|
# Single AgentRuntime boundary over the orchestrator + run store.
|
|
1508
|
+
# (lattice_brain/runtime.agent_runtime.AgentRuntime — see runtime/__init__.py for full dep graph + entry mapping)
|
|
1508
1509
|
AGENT_RUNTIME = AgentRuntime(
|
|
1509
1510
|
store=WORKSPACE_OS,
|
|
1510
1511
|
orchestrator_factory=PLATFORM.build_orchestrator,
|
|
@@ -19,7 +19,7 @@ from pathlib import Path
|
|
|
19
19
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
WORKSPACE_OS_VERSION = "5.
|
|
22
|
+
WORKSPACE_OS_VERSION = "5.5.0"
|
|
23
23
|
|
|
24
24
|
# Workspace types separate single-user Personal workspaces from shared
|
|
25
25
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|