ltcai 1.7.0 → 2.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 +32 -21
- package/docs/CHANGELOG.md +119 -0
- package/docs/EDITION_STRATEGY.md +10 -4
- package/docs/ENTERPRISE.md +3 -1
- package/docs/MULTI_AGENT_RUNTIME.md +428 -0
- package/docs/PLUGIN_SDK.md +664 -0
- package/docs/REALTIME_COLLABORATION.md +423 -0
- package/docs/V2_ARCHITECTURE.md +540 -0
- package/docs/WORKFLOW_DESIGNER.md +485 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +154 -0
- package/latticeai/api/marketplace.py +81 -0
- package/latticeai/api/plugins.py +115 -0
- package/latticeai/api/realtime.py +91 -0
- package/latticeai/api/workflow_designer.py +216 -0
- package/latticeai/core/marketplace.py +178 -0
- package/latticeai/core/multi_agent.py +561 -0
- package/latticeai/core/plugins.py +416 -0
- package/latticeai/core/realtime.py +190 -0
- package/latticeai/core/workflow_engine.py +329 -0
- package/latticeai/core/workspace_os.py +406 -6
- package/latticeai/server_app.py +88 -2
- package/latticeai/services/platform_runtime.py +204 -0
- package/package.json +8 -2
- package/plugins/README.md +35 -0
- package/plugins/git-insights/plugin.json +15 -0
- package/plugins/hello-world/plugin.json +16 -0
- package/plugins/hello-world/skills/hello_skill/SKILL.md +15 -0
- package/static/activity.html +70 -0
- package/static/agents.html +136 -0
- package/static/platform.css +75 -0
- package/static/plugins.html +133 -0
- package/static/scripts/platform.js +64 -0
- package/static/workflows.html +143 -0
- package/static/workspace.html +5 -1
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# Lattice AI v2 Architecture — Agentic Workspace Platform
|
|
2
|
+
|
|
3
|
+
Lattice AI v2.0.0 turned the local-first Workspace OS into a full **Agentic
|
|
4
|
+
Workspace Platform**. v2.1.0 keeps that architecture and matures the operational
|
|
5
|
+
layer: explicit handoffs, context packets, review/retry loops, memory snapshots,
|
|
6
|
+
planning records, replay, marketplace templates, and realtime execution
|
|
7
|
+
observability all compose over the same local-first JSON store and Knowledge
|
|
8
|
+
Graph.
|
|
9
|
+
|
|
10
|
+
This document describes how the four v2.0 pillars fit together, the small set of
|
|
11
|
+
**additive integration seams** that wire them, the cross-integration matrix that
|
|
12
|
+
results, and the compatibility surfaces that v1.x callers and data keep relying
|
|
13
|
+
on. Every claim below is grounded in the shipping source:
|
|
14
|
+
|
|
15
|
+
- App assembly: `latticeai/server_app.py`
|
|
16
|
+
- Cross-system wiring: `latticeai/services/platform_runtime.py`
|
|
17
|
+
- State + persistence: `latticeai/core/workspace_os.py`
|
|
18
|
+
- Plugin SDK: `latticeai/core/plugins.py`, `latticeai/api/plugins.py`
|
|
19
|
+
- Workflow engine: `latticeai/core/workflow_engine.py`, `latticeai/api/workflow_designer.py`
|
|
20
|
+
- Multi-Agent runtime: `latticeai/core/multi_agent.py`, `latticeai/api/agents.py`
|
|
21
|
+
- Realtime bus: `latticeai/core/realtime.py`, `latticeai/api/realtime.py`
|
|
22
|
+
- Marketplace foundation: `latticeai/core/marketplace.py`, `latticeai/api/marketplace.py`
|
|
23
|
+
- Project conventions: `AGENTS.md`
|
|
24
|
+
|
|
25
|
+
All four subsystems share the same design rules from `AGENTS.md`: dependency
|
|
26
|
+
injection, explicit interfaces, small focused modules, registry-based dispatch,
|
|
27
|
+
and composition over global state. None of them import the FastAPI app; each is
|
|
28
|
+
constructed by `server_app.py` and exposed through a router factory.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. The Four v2 Pillars
|
|
33
|
+
|
|
34
|
+
The platform version is the single source of truth `WORKSPACE_OS_VERSION =
|
|
35
|
+
"2.1.0"` (`latticeai/core/workspace_os.py`). Each pillar module re-declares the
|
|
36
|
+
same version for its own surface (`PLUGIN_SDK_VERSION`, `WORKFLOW_ENGINE_VERSION`,
|
|
37
|
+
`MULTI_AGENT_VERSION`, `REALTIME_VERSION`) and the marketplace foundation exposes
|
|
38
|
+
`MARKETPLACE_VERSION`.
|
|
39
|
+
|
|
40
|
+
### 1.1 Plugin SDK (`latticeai.core.plugins`)
|
|
41
|
+
|
|
42
|
+
A plugin is a directory under the configured plugins root that ships a
|
|
43
|
+
`plugin.json` manifest and **extends** the existing Skill / Tool / Workflow
|
|
44
|
+
surfaces rather than replacing them. Installed standalone skills keep working
|
|
45
|
+
untouched; a plugin simply *bundles* skills and declares tools / workflow
|
|
46
|
+
templates / actions under one versioned, permissioned unit.
|
|
47
|
+
|
|
48
|
+
Design rules enforced by the module: no import-time I/O (the filesystem is only
|
|
49
|
+
touched on `discover()`), no FastAPI and no globals (lifecycle state lives in the
|
|
50
|
+
Workspace OS store), and permissions are an allow-list — the execution boundary
|
|
51
|
+
refuses any capability a plugin did not declare *and* was not granted.
|
|
52
|
+
|
|
53
|
+
A validated `plugin.json` manifest:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"id": "release-notes",
|
|
58
|
+
"name": "Release Notes Assistant",
|
|
59
|
+
"version": "1.0.0",
|
|
60
|
+
"description": "Bundles a release-notes skill and a packaging workflow.",
|
|
61
|
+
"author": "you@example.com",
|
|
62
|
+
"lattice_version": ">=2.0.0",
|
|
63
|
+
"permissions": ["read_workspace", "run_skills", "run_workflows"],
|
|
64
|
+
"provides": {
|
|
65
|
+
"skills": ["release-notes"],
|
|
66
|
+
"workflows": ["package-and-tag"],
|
|
67
|
+
"tools": [],
|
|
68
|
+
"actions": []
|
|
69
|
+
},
|
|
70
|
+
"entrypoint": "",
|
|
71
|
+
"homepage": ""
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Constraints from `validate_manifest` and the module constants:
|
|
76
|
+
|
|
77
|
+
- `id`: lowercase alphanumeric with `-`/`_`, 2–64 chars (`_ID_RE`).
|
|
78
|
+
- `version`: semantic version (`_SEMVER_RE`).
|
|
79
|
+
- `permissions` ⊆ `PLUGIN_PERMISSIONS` = `read_workspace`, `write_workspace`,
|
|
80
|
+
`read_graph`, `write_graph`, `run_tools`, `run_skills`, `run_workflows`,
|
|
81
|
+
`run_agents`, `network`, `manage_memory`.
|
|
82
|
+
- `provides` keys ⊆ `PLUGIN_PROVIDES` = `skills`, `tools`, `workflows`,
|
|
83
|
+
`actions`; each value is a list.
|
|
84
|
+
- `lattice_version` is a minimum (major must match host, host must be `>=`),
|
|
85
|
+
checked by `is_compatible(required, current=PLUGIN_SDK_VERSION)`.
|
|
86
|
+
|
|
87
|
+
The permissioned execution boundary:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
def execute_action(
|
|
91
|
+
self,
|
|
92
|
+
plugin_id: str,
|
|
93
|
+
action: str,
|
|
94
|
+
args: Optional[Dict[str, Any]] = None,
|
|
95
|
+
*,
|
|
96
|
+
runners: Optional[Dict[str, Callable[..., Any]]] = None,
|
|
97
|
+
) -> PluginExecutionResult: ...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`execute_action` maps an action to the capability + permission it needs
|
|
101
|
+
(`run_tool→tools/run_tools`, `run_skill→skills/run_skills`,
|
|
102
|
+
`run_workflow→workflows/run_workflows`, `run_agent→agents/run_agents`). It
|
|
103
|
+
returns a `PluginExecutionResult` with `status` in `ok | blocked | error |
|
|
104
|
+
skipped`:
|
|
105
|
+
|
|
106
|
+
- `blocked` if the plugin is not enabled, did not declare the required
|
|
107
|
+
permission, or the permission was not granted at install time.
|
|
108
|
+
- `skipped` if the host injected no runner for the capability (never crashes the
|
|
109
|
+
caller — safe-by-default).
|
|
110
|
+
- `error` if the runner raised; `ok` otherwise.
|
|
111
|
+
|
|
112
|
+
### 1.2 Workflow Designer (`latticeai.core.workflow_engine`)
|
|
113
|
+
|
|
114
|
+
A workflow is a small directed graph of typed *nodes* starting from a `trigger`.
|
|
115
|
+
`NODE_TYPES` = `trigger`, `tool`, `skill`, `plugin`, `agent`, `condition`,
|
|
116
|
+
`output`. The engine walks the graph from the trigger, dispatching each
|
|
117
|
+
executable node to an injected runner and recording a step-by-step timeline so a
|
|
118
|
+
run can be inspected, replayed, and linked into the Workspace timeline and
|
|
119
|
+
Knowledge Graph.
|
|
120
|
+
|
|
121
|
+
Key safety properties baked into the engine:
|
|
122
|
+
|
|
123
|
+
- **Bounded execution.** `_MAX_STEPS = 100` is a hard cap, so a mis-wired `next`
|
|
124
|
+
cycle can never hang a run; exceeding it records a `guard` error.
|
|
125
|
+
- **Graceful degradation.** A node whose runner family is not wired is recorded
|
|
126
|
+
as `skipped` with a reason rather than failing the whole run; the run status
|
|
127
|
+
becomes `partial`.
|
|
128
|
+
- **No `eval`.** `condition` nodes use `_evaluate_condition`, which compares a
|
|
129
|
+
context value to a literal via a fixed op set (`==`, `!=`, `>`, `<`, `>=`,
|
|
130
|
+
`<=`, `contains`, `truthy`) and fails closed onto the `false` branch.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
class WorkflowEngine:
|
|
134
|
+
def __init__(self, runners: Optional[Dict[str, Callable[..., Any]]] = None): ...
|
|
135
|
+
def run(self, workflow: Dict[str, Any], *, inputs: Optional[Dict[str, Any]] = None) -> WorkflowRun: ...
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
A run yields a `WorkflowRun` with `status` in `ok | failed | partial`, a
|
|
139
|
+
`timeline`, and `outputs`. `export_workflow` / `import_workflow` provide a
|
|
140
|
+
portable JSON representation (definition only — no run history or scope).
|
|
141
|
+
|
|
142
|
+
### 1.3 Multi-Agent Runtime 2.0 (`latticeai.core.multi_agent`)
|
|
143
|
+
|
|
144
|
+
v1.x shipped a single-agent state machine (`latticeai.core.agent.AgentRuntime`:
|
|
145
|
+
PLAN → EXECUTE → VERIFY → DONE). v2.0 adds the **orchestration** layer above it:
|
|
146
|
+
a pipeline of named roles that hand off to one another, retry on a failing
|
|
147
|
+
review, and emit a structured timeline that drops straight into the Workspace
|
|
148
|
+
timeline / Knowledge Graph.
|
|
149
|
+
|
|
150
|
+
`AGENT_ROLES` = `researcher`, `planner`, `executor`, `reviewer`, `release`. The
|
|
151
|
+
`CORE_PIPELINE` is `planner → executor → reviewer`; `researcher` and `release`
|
|
152
|
+
are optional stages. Role ids match `DEFAULT_AGENTS` in
|
|
153
|
+
`latticeai/core/workspace_os.py` (`agent:planner`, `agent:executor`,
|
|
154
|
+
`agent:reviewer`, `agent:researcher`, `agent:release`).
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
class MultiAgentOrchestrator:
|
|
158
|
+
def __init__(self, role_runner: Optional[Callable[[str, OrchestrationContext], Dict[str, Any]]] = None): ...
|
|
159
|
+
def run(
|
|
160
|
+
self,
|
|
161
|
+
goal: str,
|
|
162
|
+
*,
|
|
163
|
+
user_email: Optional[str] = None,
|
|
164
|
+
workspace_id: Optional[str] = None,
|
|
165
|
+
inputs: Optional[Dict[str, Any]] = None,
|
|
166
|
+
roles: Optional[List[str]] = None,
|
|
167
|
+
max_retries: int = 2,
|
|
168
|
+
) -> AgentRunResult: ...
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Like the v1 runtime, the orchestrator is pure logic over an injected
|
|
172
|
+
`role_runner` port, so it runs with no LLM and no server. `default_role_runner`
|
|
173
|
+
is deterministic and genuinely useful: the `researcher` pulls workspace context,
|
|
174
|
+
the `planner` decomposes the goal, the `executor` carries out steps (and can
|
|
175
|
+
drive an injected `workflow_runner` / `plugin_runner`), and the `reviewer`
|
|
176
|
+
returns `pass` / `retry`. The reviewer can rewind the pipeline to the executor up
|
|
177
|
+
to `max_retries` times; the final `status` is `ok`, `retried_ok`, or `failed`.
|
|
178
|
+
|
|
179
|
+
### 1.4 Realtime Collaboration (`latticeai.core.realtime`)
|
|
180
|
+
|
|
181
|
+
An in-process pub/sub bus, presence registry, and activity feed delivered over
|
|
182
|
+
Server-Sent Events (SSE). SSE is chosen deliberately: the codebase already
|
|
183
|
+
streams model output over SSE, it needs no extra dependency, and it works through
|
|
184
|
+
the existing single-port local-first deployment.
|
|
185
|
+
|
|
186
|
+
Guarantees from the module:
|
|
187
|
+
|
|
188
|
+
- **Single-user local mode keeps working.** Publishing with zero subscribers is
|
|
189
|
+
a no-op; a `_FEED_LIMIT`-bounded ring buffer is still maintained so a late
|
|
190
|
+
subscriber catches up via `recent()` / a replay tail on `stream()`.
|
|
191
|
+
- **Workspace isolation preserved.** Every event carries `workspace_id`; a
|
|
192
|
+
subscriber only receives events whose workspace is in its allowed scope set.
|
|
193
|
+
A `None` scope (personal / local view) sees unscoped + personal events.
|
|
194
|
+
- **Backpressure-safe.** Per-subscriber queues are bounded (`_QUEUE_MAX`); on
|
|
195
|
+
overflow the oldest event is dropped rather than blocking the publisher.
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
class RealtimeBus:
|
|
199
|
+
def publish(self, event: Dict[str, Any]) -> Dict[str, Any]: ...
|
|
200
|
+
def __call__(self, event: Dict[str, Any]) -> Dict[str, Any]: # stable event_sink alias
|
|
201
|
+
return self.publish(event)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 2. How the Pillars Compose Into One Platform
|
|
207
|
+
|
|
208
|
+
The four pillars are not parallel silos. They are stitched into one platform by
|
|
209
|
+
exactly **three additive seams**, all introduced without changing any existing
|
|
210
|
+
behavior.
|
|
211
|
+
|
|
212
|
+
### Seam 1 — Two new state keys with deep-merge backfill
|
|
213
|
+
|
|
214
|
+
`WorkspaceOSStore._default_state()` adds two new top-level keys to the local-first
|
|
215
|
+
JSON state: **`plugin_registry`** (an object, mirroring `skill_registry`) and
|
|
216
|
+
**`workflow_runs`** (a list, alongside the existing `workflows`). The default
|
|
217
|
+
state also adds v2.0 feature flags (`plugin_sdk`, `workflow_designer`,
|
|
218
|
+
`multi_agent_runtime`, `realtime_collaboration`) and a `plugins` navigation area.
|
|
219
|
+
|
|
220
|
+
These are safe for existing data because `load_state()` runs `_deep_merge(default,
|
|
221
|
+
loaded)` on every load. `_deep_merge` walks the default tree and fills in any key
|
|
222
|
+
missing from the loaded file while preserving every value already present:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
def _deep_merge(default: Any, loaded: Any) -> Any:
|
|
226
|
+
if isinstance(default, dict) and isinstance(loaded, dict):
|
|
227
|
+
merged = {key: _deep_merge(value, loaded.get(key)) for key, value in default.items()}
|
|
228
|
+
for key, value in loaded.items():
|
|
229
|
+
if key not in merged:
|
|
230
|
+
merged[key] = value
|
|
231
|
+
return merged
|
|
232
|
+
if loaded is None:
|
|
233
|
+
return default
|
|
234
|
+
return loaded
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
A v1.x `workspace_os.json` that has no `plugin_registry` / `workflow_runs` is
|
|
238
|
+
therefore upgraded *in memory* on first load — the new keys are backfilled with
|
|
239
|
+
their defaults, every pre-existing snapshot, trace, memory, agent run, workflow,
|
|
240
|
+
and skill entry is preserved, and the file is only rewritten on the next normal
|
|
241
|
+
`save_state`. The Plugin SDK lifecycle helpers (`list_plugin_registry`,
|
|
242
|
+
`set_plugin_enabled`, `mark_plugin_installed`, `mark_plugin_uninstalled`) and
|
|
243
|
+
workflow-run helpers (`record_workflow_run`, `list_workflow_runs`) operate on
|
|
244
|
+
these keys, deliberately mirroring the existing skill-registry contract.
|
|
245
|
+
|
|
246
|
+
### Seam 2 — A single `event_sink` on `record_timeline_event`
|
|
247
|
+
|
|
248
|
+
`WorkspaceOSStore.__init__` takes one optional `event_sink` hook. Every state
|
|
249
|
+
mutation in the store already funnels through `record_timeline_event(area,
|
|
250
|
+
event_type, payload, workspace_id)`. v2.0 adds a single best-effort call at the
|
|
251
|
+
end of that method:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
def record_timeline_event(self, area, event_type, payload, workspace_id=None):
|
|
255
|
+
...
|
|
256
|
+
state.setdefault("timeline", []).append(event)
|
|
257
|
+
state["timeline"] = state["timeline"][-500:]
|
|
258
|
+
self.save_state(state)
|
|
259
|
+
if self.event_sink is not None:
|
|
260
|
+
try:
|
|
261
|
+
self.event_sink(event)
|
|
262
|
+
except Exception:
|
|
263
|
+
# Realtime delivery is best-effort and must never break a write.
|
|
264
|
+
pass
|
|
265
|
+
return event
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
In `server_app.py` the bus is constructed first and injected as that sink:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
REALTIME_BUS = RealtimeBus()
|
|
272
|
+
WORKSPACE_OS = WorkspaceOSStore(DATA_DIR, event_sink=REALTIME_BUS)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Because `RealtimeBus.__call__` aliases `publish`, **every** timeline event —
|
|
276
|
+
workspace, snapshot, memory, graph trace, agent run, workflow run, plugin
|
|
277
|
+
install/enable, skill change, onboarding, presence — fans into the realtime feed
|
|
278
|
+
automatically. There is no per-call instrumentation and no duplicated event
|
|
279
|
+
system. The hook defaults to `None`, so existing callers and tests see zero
|
|
280
|
+
behavior change.
|
|
281
|
+
|
|
282
|
+
### Seam 3 — `PlatformRuntime` as the one cross-wiring point
|
|
283
|
+
|
|
284
|
+
`latticeai/services/platform_runtime.py` is the single place the four subsystems
|
|
285
|
+
cross-wire to one another and to the workspace. Keeping it out of `server_app`
|
|
286
|
+
honours the `AGENTS.md` preference for small, composable, independently testable
|
|
287
|
+
modules; `server_app` only constructs it and mounts routers.
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
PLATFORM = PlatformRuntime(
|
|
291
|
+
store=WORKSPACE_OS,
|
|
292
|
+
workspace_service=WORKSPACE_SERVICE,
|
|
293
|
+
plugin_registry=PLUGIN_REGISTRY,
|
|
294
|
+
get_current_user=get_current_user,
|
|
295
|
+
workspace_graph=_workspace_graph,
|
|
296
|
+
workspace_scope_from_request=_workspace_scope_from_request,
|
|
297
|
+
get_tool_permission=get_tool_permission,
|
|
298
|
+
)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`PlatformRuntime` provides:
|
|
302
|
+
|
|
303
|
+
- **Request gating** — `gate_read` / `gate_write` resolve a caller's workspace
|
|
304
|
+
scope via `WorkspaceService` (raising `403` on `PermissionError`), and
|
|
305
|
+
`allowed_scopes` returns the set of workspaces a user may see (used by the
|
|
306
|
+
realtime router).
|
|
307
|
+
- **Plugin lifecycle hooks** — `register_plugin_skill` marks a bundled skill
|
|
308
|
+
installed in the shared skill registry (`version="plugin:<id>"`), so plugins
|
|
309
|
+
extend the existing skill surface instead of owning a parallel one.
|
|
310
|
+
- **Shared node runners** — `_tool_node_runner`, `_skill_node_runner`,
|
|
311
|
+
`_plugin_node_runner`, `_agent_node_runner`, plus `plugin_capability_runners`
|
|
312
|
+
(the runner map the Plugin SDK boundary dispatches to) and `_context_provider`
|
|
313
|
+
(feeds workspace memory to the agent researcher role).
|
|
314
|
+
- **Cross-system runs** — `run_workflow_by_id` and `run_agent`, plus the
|
|
315
|
+
factories `build_workflow_runners`, `build_orchestrator`, and
|
|
316
|
+
`plugin_capability_runners` that are handed to the routers.
|
|
317
|
+
|
|
318
|
+
The four routers are wired entirely through `PLATFORM`:
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
app.include_router(create_plugins_router(
|
|
322
|
+
registry=PLUGIN_REGISTRY,
|
|
323
|
+
register_skill=PLATFORM.register_plugin_skill,
|
|
324
|
+
plugin_runners_factory=lambda: PLATFORM.plugin_capability_runners(None, None),
|
|
325
|
+
...
|
|
326
|
+
))
|
|
327
|
+
app.include_router(create_workflow_designer_router(
|
|
328
|
+
store=WORKSPACE_OS,
|
|
329
|
+
gate_read=PLATFORM.gate_read, gate_write=PLATFORM.gate_write,
|
|
330
|
+
build_runners=PLATFORM.build_workflow_runners,
|
|
331
|
+
...
|
|
332
|
+
))
|
|
333
|
+
app.include_router(create_agents_router(
|
|
334
|
+
store=WORKSPACE_OS,
|
|
335
|
+
orchestrator_factory=PLATFORM.build_orchestrator,
|
|
336
|
+
gate_read=PLATFORM.gate_read, gate_write=PLATFORM.gate_write,
|
|
337
|
+
...
|
|
338
|
+
))
|
|
339
|
+
app.include_router(create_realtime_router(
|
|
340
|
+
bus=REALTIME_BUS,
|
|
341
|
+
allowed_scopes=PLATFORM.allowed_scopes,
|
|
342
|
+
...
|
|
343
|
+
))
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 3. Cross-Integration Matrix
|
|
349
|
+
|
|
350
|
+
Because of the seams above, the subsystems can drive one another. The following
|
|
351
|
+
capabilities are all backed by `PlatformRuntime` runners and the engines:
|
|
352
|
+
|
|
353
|
+
| From | Can run | Wired by |
|
|
354
|
+
| --- | --- | --- |
|
|
355
|
+
| **Workflow** node | `tool`, `skill`, `plugin`, `agent` | `build_workflow_runners` → `_tool_node_runner`, `_skill_node_runner`, `_plugin_node_runner`, `_agent_node_runner` |
|
|
356
|
+
| **Agent** run (executor role) | `plugin`, `workflow` | `build_orchestrator` / `run_agent` inject `workflow_runner` + `plugin_runner` into `default_role_runner` |
|
|
357
|
+
| **Plugin** action | `skill`, `tool`, `workflow`, `agent` | `plugin_capability_runners` → `run_skill`, `run_tool`, `run_workflow`, `run_agent` |
|
|
358
|
+
| **Any timeline event** | Realtime feed | `event_sink` → `RealtimeBus.publish` (Seam 2) |
|
|
359
|
+
| **Graph entities** | Link to `workflow_runs`, `agent_runs` (and workflows, memories) | `record_workflow_run`, `record_agent_run`, `create_workflow` call `graph.ingest_event` and store the returned `graph_node_id` |
|
|
360
|
+
| **Workspace timeline** | Reflects all activity | every store mutation calls `record_timeline_event`; `timeline()` also re-projects snapshots, traces, agent runs, and workflows |
|
|
361
|
+
|
|
362
|
+
Concrete flows:
|
|
363
|
+
|
|
364
|
+
- **Workflow → tool/skill/plugin/agent.** The Workflow Designer `run` endpoint
|
|
365
|
+
builds the runner map from `PLATFORM.build_workflow_runners(user, scope)` and
|
|
366
|
+
passes it to `WorkflowEngine`. A `tool` node records the invocation plus its
|
|
367
|
+
governance decision (via `get_tool_permission`) but never silently executes
|
|
368
|
+
exec/destructive tools. A `plugin` node calls `registry.execute_action(...)`
|
|
369
|
+
through the permission boundary. An `agent` node calls `run_agent(...,
|
|
370
|
+
with_workflow=False)`.
|
|
371
|
+
|
|
372
|
+
- **Agent → plugin/workflow.** `run_agent` constructs `default_role_runner` with
|
|
373
|
+
a `workflow_runner` (`run_workflow_by_id(..., with_agent=False)`) and a
|
|
374
|
+
`plugin_runner` (`registry.execute_action(pid, "run_skill", ...)`), so the
|
|
375
|
+
executor role can drive both. The result is persisted with
|
|
376
|
+
`store.record_agent_run`.
|
|
377
|
+
|
|
378
|
+
- **Plugin → workflow/agent.** `plugin_capability_runners` exposes `run_workflow`
|
|
379
|
+
(delegates to `run_workflow_by_id(..., with_agent=False)`) and `run_agent`
|
|
380
|
+
(delegates to `run_agent(..., with_workflow=False)`), each gated by the
|
|
381
|
+
plugin's declared permission.
|
|
382
|
+
|
|
383
|
+
- **Graph linkage.** When the Knowledge Graph is enabled, `record_workflow_run`
|
|
384
|
+
ingests a `WorkflowRun` node, `record_agent_run` ingests an `AgentRun` node,
|
|
385
|
+
and `create_workflow` ingests a `Workflow` node — each storing the returned
|
|
386
|
+
`graph_node_id` on the record so runs are navigable from the graph and via the
|
|
387
|
+
Relationship Explorer.
|
|
388
|
+
|
|
389
|
+
- **Unified timeline.** `WorkspaceOSStore.timeline()` merges live timeline events
|
|
390
|
+
with re-projected snapshots, answer traces, agent runs, and workflows (and
|
|
391
|
+
optional audit events), all scope-filtered, giving one chronological view of
|
|
392
|
+
every subsystem's activity.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## 4. Recursion Bounding
|
|
397
|
+
|
|
398
|
+
Cross-system runs could in principle recurse forever (workflow runs an agent that
|
|
399
|
+
runs a workflow that runs an agent…). v2.0 bounds recursion **by construction** in
|
|
400
|
+
`PlatformRuntime`, not by a runtime depth counter:
|
|
401
|
+
|
|
402
|
+
- A workflow's **`agent` node** runs an orchestrator built *without* a workflow
|
|
403
|
+
runner. In `run_agent` the workflow node path calls
|
|
404
|
+
`run_agent(..., with_workflow=False)`, so `default_role_runner` receives
|
|
405
|
+
`workflow_runner=None`.
|
|
406
|
+
- An orchestrator's **workflow runner** runs an engine built *without* an `agent`
|
|
407
|
+
runner. `run_workflow_by_id(..., with_agent=False)` assembles the runner map
|
|
408
|
+
with only `tool`, `skill`, and `plugin` — no `agent` key — so the
|
|
409
|
+
`WorkflowEngine` skips any `agent` node it encounters.
|
|
410
|
+
|
|
411
|
+
The deepest possible chains are therefore:
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
agent → workflow → (tool | skill | plugin)
|
|
415
|
+
workflow → agent → plugin
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Independently, the `WorkflowEngine` enforces `_MAX_STEPS = 100` per run as a hard
|
|
419
|
+
cap against `next`-pointer cycles, and `MultiAgentOrchestrator` caps reviewer
|
|
420
|
+
retries via `max_retries` (clamped to 0–5 in the agents router). Together these
|
|
421
|
+
guarantee every cross-system run terminates.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 5. HTTP Surface (v2 additions)
|
|
426
|
+
|
|
427
|
+
All v2 routes are namespaced so they never collide with existing paths
|
|
428
|
+
(`/plugins/registry` vs. the marketplace `/plugins/directory`; `/workflows` vs.
|
|
429
|
+
`/workspace/workflows`; `/agents` plural vs. the single-agent `/agent`).
|
|
430
|
+
|
|
431
|
+
**Plugin SDK** (`latticeai/api/plugins.py`): `GET /plugins/sdk`,
|
|
432
|
+
`GET /plugins/registry`, `GET /plugins/registry/{plugin_id}`,
|
|
433
|
+
`POST /plugins/validate`, `POST /plugins/install` (admin),
|
|
434
|
+
`POST /plugins/uninstall` (admin), `POST /plugins/enable`,
|
|
435
|
+
`POST /plugins/disable`, `POST /plugins/execute`.
|
|
436
|
+
|
|
437
|
+
**Workflow Designer** (`latticeai/api/workflow_designer.py`): `GET /workflows`,
|
|
438
|
+
`GET|POST /workflows/api/definitions`, `GET|PATCH
|
|
439
|
+
/workflows/api/definitions/{id}`, `POST /workflows/api/validate`,
|
|
440
|
+
`POST /workflows/api/definitions/{id}/run`,
|
|
441
|
+
`GET /workflows/api/definitions/{id}/runs`, `GET /workflows/api/runs`,
|
|
442
|
+
`GET /workflows/api/runs/{run_id}/replay`,
|
|
443
|
+
`GET /workflows/api/export/{id}`, `POST /workflows/api/import`.
|
|
444
|
+
|
|
445
|
+
**Multi-Agent Runtime** (`latticeai/api/agents.py`): `GET /agents`,
|
|
446
|
+
`GET /agents/api/roles`, `GET /agents/api/runs`,
|
|
447
|
+
`GET /agents/api/runs/{run_id}/replay`, `GET /agents/api/handoffs`,
|
|
448
|
+
`GET|POST /agents/api/memory/snapshots`, `POST /agents/api/run`.
|
|
449
|
+
|
|
450
|
+
**Marketplace foundation** (`latticeai/api/marketplace.py`):
|
|
451
|
+
`GET /marketplace/templates`, `GET /marketplace/templates/{kind}/{id}/export`,
|
|
452
|
+
`POST /marketplace/templates/import`, `POST /marketplace/templates/install`,
|
|
453
|
+
`GET /marketplace/templates/registry`.
|
|
454
|
+
|
|
455
|
+
**Realtime Collaboration** (`latticeai/api/realtime.py`): `GET /activity`,
|
|
456
|
+
`GET /realtime/stream` (SSE), `GET /realtime/feed`, `GET /realtime/presence`,
|
|
457
|
+
`POST /realtime/presence/join`, `POST /realtime/presence/leave`.
|
|
458
|
+
|
|
459
|
+
Representative run request/response (Workflow Designer):
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
// POST /workflows/api/definitions/{workflow_id}/run
|
|
463
|
+
{ "inputs": { "steps": ["analyze", "package"] } }
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
```json
|
|
467
|
+
{
|
|
468
|
+
"run": { "id": "workflow-run-…", "status": "ok", "workflow_id": "workflow-…" },
|
|
469
|
+
"result": { "status": "ok", "step_count": 4, "timeline": [ /* per-node */ ], "outputs": {} }
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
```json
|
|
474
|
+
// POST /agents/api/run
|
|
475
|
+
{ "goal": "Draft v2.1 release notes", "roles": ["planner", "executor", "reviewer"], "max_retries": 2 }
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
```json
|
|
479
|
+
{
|
|
480
|
+
"run": { "id": "agent-run-…", "agent_id": "agent:executor", "status": "ok" },
|
|
481
|
+
"result": { "status": "ok", "roles_run": ["planner", "executor", "reviewer"], "retries": 0, "output": "…" }
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## 6. Compatibility
|
|
488
|
+
|
|
489
|
+
> **Compatibility note.** v2.1.0 is **additive**. All v1.x and v2.0 data and APIs are
|
|
490
|
+
> preserved; the platform layers new capabilities on top of unchanged surfaces.
|
|
491
|
+
|
|
492
|
+
Preserved surfaces, verified against source:
|
|
493
|
+
|
|
494
|
+
- **ASGI entrypoints.** `server:app` and `latticeai.server_app.app` remain the
|
|
495
|
+
application objects. `server_app.py` still exposes the module-level `app =
|
|
496
|
+
FastAPI(...)` plus the `main()` / `uvicorn.run(app, ...)` entry point.
|
|
497
|
+
- **Version wiring.** `WORKSPACE_OS_VERSION = "2.1.0"` drives both
|
|
498
|
+
`APP_VERSION` (and thus the FastAPI `app.version`) and the `/health`
|
|
499
|
+
response — the health router is constructed with `app_version=APP_VERSION`.
|
|
500
|
+
- **Existing routes.** Every v1.x router (`auth`, `admin`, `security_dashboard`,
|
|
501
|
+
`workspace`, `health`, `models`, `chat`, `tools`, `garden`, `setup`,
|
|
502
|
+
`static_routes`) is still included unchanged in `server_app.py`. The v2.0
|
|
503
|
+
routers are *added* alongside them under non-colliding namespaces.
|
|
504
|
+
- **Data.** The local-first `workspace_os.json` is upgraded by deep-merge
|
|
505
|
+
backfill (Seam 1) and `_migrate_workspaces` (non-destructive workspace
|
|
506
|
+
upgrade); no migration deletes or rewrites existing snapshots, traces,
|
|
507
|
+
memories, agent runs, workflows, or skills.
|
|
508
|
+
- **Skills.** Standalone installed skills keep working; plugins reuse the same
|
|
509
|
+
`skill_registry` via `register_plugin_skill` rather than a parallel store.
|
|
510
|
+
- **Snapshots.** Snapshot files remain immutable JSON under
|
|
511
|
+
`workspace_snapshots/`; `snapshot["version"]` is stamped with
|
|
512
|
+
`WORKSPACE_OS_VERSION` but creation/compare/export behavior is unchanged.
|
|
513
|
+
- **Knowledge Graph.** Graph operations stay additive (`ingest_event`,
|
|
514
|
+
`ingest_message`); v2.0 only *adds* `WorkflowRun` / `AgentRun` linkage on top,
|
|
515
|
+
honoring the `AGENTS.md` rule to preserve legacy read compatibility and avoid
|
|
516
|
+
destructive migrations.
|
|
517
|
+
- **Legacy workflows.** Pre-2.0 workflows persisted as a flat `steps` list still
|
|
518
|
+
validate and run — `workflow_engine.normalize_definition` lifts them into a
|
|
519
|
+
linear `trigger → tool… → output` node chain without rewriting stored history.
|
|
520
|
+
- **Realtime hook is opt-in.** `WorkspaceOSStore(event_sink=...)` defaults to
|
|
521
|
+
`None`; without a bus the store behaves exactly as in v1.x.
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## 7. Per-Subsystem Reference
|
|
526
|
+
|
|
527
|
+
Each pillar is intentionally a small, self-documenting module. For the
|
|
528
|
+
authoritative, code-level definition of each subsystem, see:
|
|
529
|
+
|
|
530
|
+
- **Plugin SDK** — `latticeai/core/plugins.py` (manifest schema, permissions,
|
|
531
|
+
execution boundary) and `latticeai/api/plugins.py` (HTTP surface).
|
|
532
|
+
- **Workflow Designer** — `latticeai/core/workflow_engine.py` (node types,
|
|
533
|
+
validation, interpreter) and `latticeai/api/workflow_designer.py`.
|
|
534
|
+
- **Multi-Agent Runtime 2.0** — `latticeai/core/multi_agent.py` (roles, pipeline,
|
|
535
|
+
retry) and `latticeai/api/agents.py`.
|
|
536
|
+
- **Realtime Collaboration** — `latticeai/core/realtime.py` (bus, presence, SSE)
|
|
537
|
+
and `latticeai/api/realtime.py`.
|
|
538
|
+
- **Cross-wiring** — `latticeai/services/platform_runtime.py`.
|
|
539
|
+
- **State + persistence** — `latticeai/core/workspace_os.py`.
|
|
540
|
+
- **App assembly** — `latticeai/server_app.py`.
|