ltcai 7.1.0 → 7.2.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 +21 -19
- package/docs/CHANGELOG.md +20 -0
- package/frontend/src/api/client.ts +3 -0
- package/frontend/src/features/admin/AdminConsole.tsx +41 -1
- package/frontend/src/i18n.ts +24 -0
- package/frontend/src/styles.css +45 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/agent_runtime.py +51 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +12 -0
- package/latticeai/api/tools.py +14 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/tool_registry.py +47 -0
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/tool_dispatch.py +14 -0
- 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 +28 -28
- package/static/app/assets/{Act-BHEb8bAM.js → Act-Di4tRFWY.js} +2 -2
- package/static/app/assets/{Act-BHEb8bAM.js.map → Act-Di4tRFWY.js.map} +1 -1
- package/static/app/assets/{Brain-yvR7xkrV.js → Brain-BZB3Gy9w.js} +2 -2
- package/static/app/assets/{Brain-yvR7xkrV.js.map → Brain-BZB3Gy9w.js.map} +1 -1
- package/static/app/assets/{Capture-BrAmsSEH.js → Capture-tNyYWxnh.js} +2 -2
- package/static/app/assets/{Capture-BrAmsSEH.js.map → Capture-tNyYWxnh.js.map} +1 -1
- package/static/app/assets/{Library-BOVzYfxI.js → Library-DAtDDLdg.js} +2 -2
- package/static/app/assets/{Library-BOVzYfxI.js.map → Library-DAtDDLdg.js.map} +1 -1
- package/static/app/assets/{System-D4WaN4kj.js → System-DEu0xNUc.js} +2 -2
- package/static/app/assets/{System-D4WaN4kj.js.map → System-DEu0xNUc.js.map} +1 -1
- package/static/app/assets/{index-DvLsGy-z.css → index-Bi_bpigM.css} +1 -1
- package/static/app/assets/{index-BOB-W1FZ.js → index-COuGp7_5.js} +3 -3
- package/static/app/assets/index-COuGp7_5.js.map +1 -0
- package/static/app/assets/{primitives-C3_BfUb8.js → primitives-CdwcE--L.js} +2 -2
- package/static/app/assets/{primitives-C3_BfUb8.js.map → primitives-CdwcE--L.js.map} +1 -1
- package/static/app/assets/{textarea-CbvhHvzg.js → textarea-CqOdBPL1.js} +2 -2
- package/static/app/assets/{textarea-CbvhHvzg.js.map → textarea-CqOdBPL1.js.map} +1 -1
- package/static/app/index.html +2 -2
- package/static/app/assets/index-BOB-W1FZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -200,25 +200,26 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
|
|
|
200
200
|
|
|
201
201
|
## Current Release
|
|
202
202
|
|
|
203
|
-
The current release is **7.
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
|
|
211
|
-
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- `dist/ltcai-7.
|
|
219
|
-
- `ltcai-7.
|
|
220
|
-
- `
|
|
221
|
-
- `
|
|
203
|
+
The current release is **7.2.0 Runtime Trust Baseline**:
|
|
204
|
+
|
|
205
|
+
- Agent runs can be previewed before execution, including selected roles,
|
|
206
|
+
clamped retry budget, readiness state, and blocking reasons.
|
|
207
|
+
- Product agent execution refuses simulation-mode success unless simulation is
|
|
208
|
+
explicitly enabled for a non-product runtime.
|
|
209
|
+
- ToolRegistry exposes a live manifest and diagnostics endpoint covering
|
|
210
|
+
dispatch handlers, governance policy, catalog descriptions, and permissions.
|
|
211
|
+
- Tool governance/catalog drift is covered by unit tests so tool additions
|
|
212
|
+
cannot silently bypass policy or disappear from operator views.
|
|
213
|
+
- 7.1.0 Brain usability work remains intact while the runtime gains clearer
|
|
214
|
+
contracts for admin surfaces and future AgentRuntime extraction.
|
|
215
|
+
|
|
216
|
+
Expected artifacts for 7.2.0 release must use exact filenames:
|
|
217
|
+
|
|
218
|
+
- `dist/ltcai-7.2.0-py3-none-any.whl`
|
|
219
|
+
- `dist/ltcai-7.2.0.tar.gz`
|
|
220
|
+
- `ltcai-7.2.0.tgz`
|
|
221
|
+
- `dist/ltcai-7.2.0.vsix`
|
|
222
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_7.2.0_aarch64.dmg`
|
|
222
223
|
|
|
223
224
|
Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
224
225
|
|
|
@@ -237,6 +238,7 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
|
237
238
|
|
|
238
239
|
| Version | Theme |
|
|
239
240
|
| --- | --- |
|
|
241
|
+
| 7.2.0 | Runtime Trust Baseline: agent run preview/readiness, simulation-mode guardrails, live ToolRegistry manifest/diagnostics, and tests for dispatch/governance/catalog drift |
|
|
240
242
|
| 7.1.0 | Brain Usability Completion: clearer first-run onboarding, ingestion progress/emergence, richer graph controls, inline answer proof, workspace/profile/admin discovery, empty/error/consent feedback, and VS Code sync status |
|
|
241
243
|
| 7.0.0 | Brain Productization Loop: first-screen ingestion for files/folders/notes/web, answer-level memory proof and source citations, model-continuity demo flow, five-minute first-run loop, and recall/KG quality eval in CI |
|
|
242
244
|
| 6.7.0 | Brain IA Cleanup: reachable rich pages, separated product routes vs compatibility aliases, shared Brain shell navigation, and lazy-loaded rich pages |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ The top entry is either the current unreleased main-branch work or the current
|
|
|
4
4
|
release line. Older entries are historical and may describe behavior as it
|
|
5
5
|
existed at that release.
|
|
6
6
|
|
|
7
|
+
## [7.2.0] - 2026-06-20
|
|
8
|
+
|
|
9
|
+
> Runtime Trust Baseline. Adds execution preview and registry diagnostics so
|
|
10
|
+
> agent runs and tool permissions become inspectable contracts before action.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `POST /agents/api/run/preview` for AgentRuntime readiness, role selection,
|
|
14
|
+
input keys, retry clamping, and blocking reasons without starting a run.
|
|
15
|
+
- `GET /tools/registry` for the live ToolRegistry manifest across dispatch
|
|
16
|
+
handlers, governance policy, catalog descriptions, and permissions.
|
|
17
|
+
- `GET /tools/registry/diagnostics` for a compact drift check suitable for CI,
|
|
18
|
+
admin views, and runtime health panels.
|
|
19
|
+
- Unit coverage for AgentRuntime preview and ToolRegistry manifest diagnostics.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Tool governance now covers `read_document`, and the catalog describes
|
|
23
|
+
`create_web_project`, closing the current dispatch/governance/catalog drift.
|
|
24
|
+
- Package/runtime/static metadata is synchronized to 7.2.0; package publish and
|
|
25
|
+
deployment remain owner-run only.
|
|
26
|
+
|
|
7
27
|
## [7.1.0] - 2026-06-20
|
|
8
28
|
|
|
9
29
|
> Brain Usability Completion. Completes the 7.1.0 first-run through editor-sync
|
|
@@ -481,7 +481,10 @@ export const latticeApi = {
|
|
|
481
481
|
unloadModel: (model_id: string) => del(`/models/unload/${encodeURIComponent(model_id)}`, {}),
|
|
482
482
|
embeddingsStatus: () => get("/api/embeddings/status", {}),
|
|
483
483
|
agentRuntime: () => get("/agents/api/runtime/status", { runtime: {}, agents: [], runs: [] }),
|
|
484
|
+
agentRunPreview: (goal: string, roles: string[] = []) => post("/agents/api/run/preview", { goal, roles }, { ready: false, blocking_reasons: [] }),
|
|
484
485
|
runAgent: (goal: string, roles: string[]) => post("/agents/api/run", { goal, roles }, {}),
|
|
486
|
+
toolRegistryDiagnostics: () => get("/tools/registry/diagnostics", { diagnostics: { ready: false } }),
|
|
487
|
+
toolRegistry: () => get("/tools/registry", { status: "unavailable", diagnostics: {}, tools: [] }),
|
|
485
488
|
agentRun: (id: string) => get(`/agents/api/runs/${encodeURIComponent(id)}`, {}),
|
|
486
489
|
stopAgentRun: (id: string) => post(`/agents/api/runs/${encodeURIComponent(id)}/stop`, {}, {}),
|
|
487
490
|
agentRegistry: () => get("/agents/api/registry", { agents: [] }),
|
|
@@ -15,7 +15,7 @@ export function AdminConsole({ onBack }: { onBack: () => void }) {
|
|
|
15
15
|
const qc = useQueryClient();
|
|
16
16
|
const language = useAppStore((state) => state.language);
|
|
17
17
|
const [filters, setFilters] = React.useState<AdminFilterState>({ q: "", actor: "", action: "", severity: "", limit: 50 });
|
|
18
|
-
const { summaryQ, statsQ, usersQ, auditQ, securityQ, securityEventsQ, policiesQ, rolesQ, retentionQ, indexQ } = useAdminConsoleData(filters);
|
|
18
|
+
const { summaryQ, statsQ, usersQ, auditQ, securityQ, securityEventsQ, policiesQ, rolesQ, retentionQ, indexQ, agentRuntimeQ, toolRegistryQ } = useAdminConsoleData(filters);
|
|
19
19
|
const rebuildIndex = useMutation({
|
|
20
20
|
mutationFn: latticeApi.rebuildIndex,
|
|
21
21
|
onSuccess: () => void qc.invalidateQueries({ queryKey: ["indexStatus"] }),
|
|
@@ -139,6 +139,10 @@ export function AdminConsole({ onBack }: { onBack: () => void }) {
|
|
|
139
139
|
</div>
|
|
140
140
|
</AdminPanel>
|
|
141
141
|
|
|
142
|
+
<AdminPanel title={t(language, "admin.panel.runtimeTrust")} eyebrow={t(language, "admin.panel.contracts")}>
|
|
143
|
+
<RuntimeTrustPanel runtime={agentRuntimeQ.data?.data as ApiRecord | undefined} registry={toolRegistryQ.data?.data as ApiRecord | undefined} language={language} />
|
|
144
|
+
</AdminPanel>
|
|
145
|
+
|
|
142
146
|
</section>
|
|
143
147
|
</main>
|
|
144
148
|
);
|
|
@@ -164,9 +168,45 @@ function useAdminConsoleData(filters: AdminFilterState) {
|
|
|
164
168
|
rolesQ: useQuery({ queryKey: ["adminRoles"], queryFn: latticeApi.adminRoles }),
|
|
165
169
|
retentionQ: useQuery({ queryKey: ["adminLogRetention"], queryFn: latticeApi.adminLogRetention }),
|
|
166
170
|
indexQ: useQuery({ queryKey: ["indexStatus"], queryFn: latticeApi.indexStatus }),
|
|
171
|
+
agentRuntimeQ: useQuery({ queryKey: ["agentRuntime"], queryFn: latticeApi.agentRuntime }),
|
|
172
|
+
toolRegistryQ: useQuery({ queryKey: ["toolRegistryDiagnostics"], queryFn: latticeApi.toolRegistryDiagnostics }),
|
|
167
173
|
};
|
|
168
174
|
}
|
|
169
175
|
|
|
176
|
+
function RuntimeTrustPanel({ runtime, registry, language }: { runtime?: ApiRecord; registry?: ApiRecord; language: "ko" | "en" }) {
|
|
177
|
+
const runtimeInfo = (runtime?.runtime || {}) as ApiRecord;
|
|
178
|
+
const health = (runtime?.health || {}) as ApiRecord;
|
|
179
|
+
const diagnostics = (registry?.diagnostics || {}) as ApiRecord;
|
|
180
|
+
const ready = Boolean(runtimeInfo.ready);
|
|
181
|
+
const registryReady = Boolean(diagnostics.ready);
|
|
182
|
+
const blocking = stringValue(runtimeInfo.unavailable_reason, ready ? t(language, "admin.runtime.readyDetail") : t(language, "admin.runtime.blockedFallback"));
|
|
183
|
+
return (
|
|
184
|
+
<div className="admin-runtime-trust">
|
|
185
|
+
<div className="admin-runtime-row">
|
|
186
|
+
<strong>{t(language, "admin.runtime.agent")}</strong>
|
|
187
|
+
<span className={ready ? "is-ok" : "is-warn"}>{ready ? t(language, "admin.status.ready") : t(language, "admin.status.unavailable")}</span>
|
|
188
|
+
<small>{blocking}</small>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="admin-runtime-row">
|
|
191
|
+
<strong>{t(language, "admin.runtime.tools")}</strong>
|
|
192
|
+
<span className={registryReady ? "is-ok" : "is-warn"}>{registryReady ? t(language, "admin.runtime.aligned") : t(language, "admin.runtime.drift")}</span>
|
|
193
|
+
<small>
|
|
194
|
+
{t(language, "admin.runtime.toolCounts", {
|
|
195
|
+
registered: stringValue(diagnostics.registered_tools, "0"),
|
|
196
|
+
governed: stringValue(diagnostics.governed_tools, "0"),
|
|
197
|
+
described: stringValue(diagnostics.described_tools, "0"),
|
|
198
|
+
})}
|
|
199
|
+
</small>
|
|
200
|
+
</div>
|
|
201
|
+
<div className="admin-policy-strip">
|
|
202
|
+
<span>{t(language, "admin.runtime.mode", { mode: stringValue(runtimeInfo.mode, "unknown") })}</span>
|
|
203
|
+
<span>{t(language, "admin.runtime.execution", { mode: stringValue(runtimeInfo.execution_mode, "unknown") })}</span>
|
|
204
|
+
<span>{t(language, "admin.runtime.health", { status: stringValue(health.status, "unknown") })}</span>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
170
210
|
function AdminLogFilters({
|
|
171
211
|
filters,
|
|
172
212
|
matched,
|
package/frontend/src/i18n.ts
CHANGED
|
@@ -348,6 +348,8 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
348
348
|
"admin.panel.protection": "보호",
|
|
349
349
|
"admin.panel.brainOps": "Brain 운영",
|
|
350
350
|
"admin.panel.maintenance": "유지보수",
|
|
351
|
+
"admin.panel.runtimeTrust": "런타임 신뢰도",
|
|
352
|
+
"admin.panel.contracts": "계약",
|
|
351
353
|
"admin.empty.users": "관리 API가 보고한 사용자가 없습니다.",
|
|
352
354
|
"admin.empty.roles": "보고된 역할 매트릭스가 없습니다.",
|
|
353
355
|
"admin.empty.audit": "최근 감사 이벤트가 없습니다.",
|
|
@@ -369,6 +371,16 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
369
371
|
"admin.brain.summaryFallback": "로컬 Brain 서비스는 사용자 대화와 분리되어 있습니다.",
|
|
370
372
|
"admin.brain.rebuilding": "재구성 중",
|
|
371
373
|
"admin.brain.rebuild": "인덱스 재구성",
|
|
374
|
+
"admin.runtime.agent": "AgentRuntime",
|
|
375
|
+
"admin.runtime.tools": "ToolRegistry",
|
|
376
|
+
"admin.runtime.readyDetail": "실행 preview와 실제 실행이 준비되었습니다.",
|
|
377
|
+
"admin.runtime.blockedFallback": "실행 전 preview가 차단 사유를 반환합니다.",
|
|
378
|
+
"admin.runtime.aligned": "정렬됨",
|
|
379
|
+
"admin.runtime.drift": "점검 필요",
|
|
380
|
+
"admin.runtime.toolCounts": "등록 {registered} · 정책 {governed} · 설명 {described}",
|
|
381
|
+
"admin.runtime.mode": "mode {mode}",
|
|
382
|
+
"admin.runtime.execution": "exec {mode}",
|
|
383
|
+
"admin.runtime.health": "health {status}",
|
|
372
384
|
"admin.policy.fallback": "정책",
|
|
373
385
|
"admin.policy.quiet": "정책 API 대기 중",
|
|
374
386
|
"admin.retention.days": "{days}일 보관",
|
|
@@ -894,6 +906,8 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
894
906
|
"admin.panel.protection": "Protection",
|
|
895
907
|
"admin.panel.brainOps": "Brain Operations",
|
|
896
908
|
"admin.panel.maintenance": "Maintenance",
|
|
909
|
+
"admin.panel.runtimeTrust": "Runtime Trust",
|
|
910
|
+
"admin.panel.contracts": "Contracts",
|
|
897
911
|
"admin.empty.users": "No users reported by the admin API.",
|
|
898
912
|
"admin.empty.roles": "No role matrix reported.",
|
|
899
913
|
"admin.empty.audit": "No recent audit events.",
|
|
@@ -915,6 +929,16 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
915
929
|
"admin.brain.summaryFallback": "Local Brain services are separated from user chat.",
|
|
916
930
|
"admin.brain.rebuilding": "Rebuilding",
|
|
917
931
|
"admin.brain.rebuild": "Rebuild index",
|
|
932
|
+
"admin.runtime.agent": "AgentRuntime",
|
|
933
|
+
"admin.runtime.tools": "ToolRegistry",
|
|
934
|
+
"admin.runtime.readyDetail": "Run preview and execution are ready.",
|
|
935
|
+
"admin.runtime.blockedFallback": "Run preview returns the blocking reason before execution.",
|
|
936
|
+
"admin.runtime.aligned": "Aligned",
|
|
937
|
+
"admin.runtime.drift": "Needs review",
|
|
938
|
+
"admin.runtime.toolCounts": "Registered {registered} · governed {governed} · described {described}",
|
|
939
|
+
"admin.runtime.mode": "mode {mode}",
|
|
940
|
+
"admin.runtime.execution": "exec {mode}",
|
|
941
|
+
"admin.runtime.health": "health {status}",
|
|
918
942
|
"admin.policy.fallback": "Policy",
|
|
919
943
|
"admin.policy.quiet": "Policy API quiet",
|
|
920
944
|
"admin.retention.days": "{days} day retention",
|
package/frontend/src/styles.css
CHANGED
|
@@ -3472,6 +3472,51 @@ body {
|
|
|
3472
3472
|
font-size: 0.88rem;
|
|
3473
3473
|
}
|
|
3474
3474
|
|
|
3475
|
+
.admin-runtime-trust {
|
|
3476
|
+
display: grid;
|
|
3477
|
+
gap: 0.72rem;
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
.admin-runtime-row {
|
|
3481
|
+
display: grid;
|
|
3482
|
+
grid-template-columns: minmax(8rem, 1fr) auto;
|
|
3483
|
+
gap: 0.2rem 0.75rem;
|
|
3484
|
+
border: 1px solid hsl(var(--border) / 0.48);
|
|
3485
|
+
border-radius: 8px;
|
|
3486
|
+
background: hsl(var(--bg) / 0.35);
|
|
3487
|
+
padding: 0.72rem;
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
.admin-runtime-row strong {
|
|
3491
|
+
color: hsl(var(--fg));
|
|
3492
|
+
font-size: 0.88rem;
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
.admin-runtime-row > span {
|
|
3496
|
+
align-self: start;
|
|
3497
|
+
border-radius: 999px;
|
|
3498
|
+
padding: 0.18rem 0.48rem;
|
|
3499
|
+
font-size: 0.72rem;
|
|
3500
|
+
font-weight: 700;
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
.admin-runtime-row > small {
|
|
3504
|
+
grid-column: 1 / -1;
|
|
3505
|
+
color: hsl(var(--fg-muted));
|
|
3506
|
+
font-size: 0.78rem;
|
|
3507
|
+
line-height: 1.35;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
.admin-runtime-row .is-ok {
|
|
3511
|
+
background: hsl(151 65% 36% / 0.13);
|
|
3512
|
+
color: hsl(151 48% 32%);
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3515
|
+
.admin-runtime-row .is-warn {
|
|
3516
|
+
background: hsl(36 88% 52% / 0.14);
|
|
3517
|
+
color: hsl(30 82% 35%);
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3475
3520
|
.brain-document-input,
|
|
3476
3521
|
.brain-image-input {
|
|
3477
3522
|
display: inline-flex;
|
|
@@ -228,6 +228,57 @@ class AgentRuntime:
|
|
|
228
228
|
"runs": runs[:25],
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
def preview(
|
|
232
|
+
self,
|
|
233
|
+
goal: str,
|
|
234
|
+
*,
|
|
235
|
+
roles: Optional[List[str]] = None,
|
|
236
|
+
inputs: Optional[Dict[str, Any]] = None,
|
|
237
|
+
max_retries: int = 2,
|
|
238
|
+
scope: Optional[str] = None,
|
|
239
|
+
) -> Dict[str, Any]:
|
|
240
|
+
"""Return the execution contract without reserving or starting a run.
|
|
241
|
+
|
|
242
|
+
The preview is the product-facing readiness gate for agent execution:
|
|
243
|
+
clients can show whether a real LLM-backed run can start, which roles
|
|
244
|
+
will execute, how retry limits are clamped, and why the run is blocked.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
requested_roles = list(roles or CORE_PIPELINE)
|
|
248
|
+
unknown_roles = [role for role in requested_roles if role not in AGENT_ROLES]
|
|
249
|
+
health = self.health()
|
|
250
|
+
goal_ready = bool(str(goal or "").strip())
|
|
251
|
+
retry_budget = max(0, min(int(max_retries or 0), self._max_retries_cap))
|
|
252
|
+
blocking_reasons: List[str] = []
|
|
253
|
+
if not goal_ready:
|
|
254
|
+
blocking_reasons.append("goal is required")
|
|
255
|
+
if unknown_roles:
|
|
256
|
+
blocking_reasons.append(f"unknown roles: {', '.join(unknown_roles)}")
|
|
257
|
+
if not health.get("ready"):
|
|
258
|
+
orchestrator = (health.get("checks") or {}).get("orchestrator") or {}
|
|
259
|
+
blocking_reasons.append(str(orchestrator.get("detail") or "agent runtime is unavailable"))
|
|
260
|
+
can_start = not blocking_reasons
|
|
261
|
+
return {
|
|
262
|
+
"ready": can_start,
|
|
263
|
+
"can_start": can_start,
|
|
264
|
+
"blocking_reasons": blocking_reasons,
|
|
265
|
+
"goal": str(goal or "").strip(),
|
|
266
|
+
"roles": requested_roles,
|
|
267
|
+
"unknown_roles": unknown_roles,
|
|
268
|
+
"inputs_keys": sorted((inputs or {}).keys()),
|
|
269
|
+
"max_retries": retry_budget,
|
|
270
|
+
"max_retries_requested": max_retries,
|
|
271
|
+
"scope": scope,
|
|
272
|
+
"execution_mode": self._execution_mode(),
|
|
273
|
+
"runtime": {
|
|
274
|
+
"version": MULTI_AGENT_VERSION,
|
|
275
|
+
"default_pipeline": list(CORE_PIPELINE),
|
|
276
|
+
"max_retries_cap": self._max_retries_cap,
|
|
277
|
+
"simulation_runs_allowed": self._allow_simulation_runs,
|
|
278
|
+
},
|
|
279
|
+
"health": health,
|
|
280
|
+
}
|
|
281
|
+
|
|
231
282
|
# ── events / state ────────────────────────────────────────────────────
|
|
232
283
|
def list_runs(self, *, scope: Optional[str] = None) -> Dict[str, Any]:
|
|
233
284
|
return self._store.list_agents(workspace_id=scope)
|
|
@@ -19,7 +19,7 @@ from datetime import datetime
|
|
|
19
19
|
from typing import Any, Callable, Dict, List, Optional
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
MULTI_AGENT_VERSION = "7.
|
|
22
|
+
MULTI_AGENT_VERSION = "7.2.0"
|
|
23
23
|
|
|
24
24
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
25
25
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
package/latticeai/api/agents.py
CHANGED
|
@@ -192,4 +192,16 @@ def create_agents_router(
|
|
|
192
192
|
# A pre_run hook gated this run (e.g. a policy/permission hook).
|
|
193
193
|
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
194
194
|
|
|
195
|
+
@router.post("/agents/api/run/preview")
|
|
196
|
+
async def agent_run_preview(req: AgentRunRequest, request: Request):
|
|
197
|
+
require_user(request)
|
|
198
|
+
scope = gate_read(request)
|
|
199
|
+
return runtime.preview(
|
|
200
|
+
req.goal,
|
|
201
|
+
scope=scope,
|
|
202
|
+
roles=req.roles or None,
|
|
203
|
+
inputs=req.inputs,
|
|
204
|
+
max_retries=req.max_retries,
|
|
205
|
+
)
|
|
206
|
+
|
|
195
207
|
return router
|
package/latticeai/api/tools.py
CHANGED
|
@@ -25,6 +25,8 @@ from latticeai.services.tool_dispatch import (
|
|
|
25
25
|
check_tool_role as _check_tool_role,
|
|
26
26
|
get_tool_permission,
|
|
27
27
|
list_tool_permissions,
|
|
28
|
+
tool_registry_diagnostics,
|
|
29
|
+
tool_registry_manifest,
|
|
28
30
|
)
|
|
29
31
|
from latticeai.services.router_context import ToolRouterContext
|
|
30
32
|
from latticeai.services.p_reinforce import BRAIN_DIR
|
|
@@ -602,6 +604,18 @@ def create_tools_router(
|
|
|
602
604
|
"""
|
|
603
605
|
require_user(request)
|
|
604
606
|
return {"status": "ok", "permissions": list_tool_permissions()}
|
|
607
|
+
|
|
608
|
+
@api_router.get("/tools/registry")
|
|
609
|
+
async def tools_registry(request: Request):
|
|
610
|
+
"""Full ToolRegistry contract: handlers, governance, catalog, diagnostics."""
|
|
611
|
+
require_user(request)
|
|
612
|
+
return tool_registry_manifest()
|
|
613
|
+
|
|
614
|
+
@api_router.get("/tools/registry/diagnostics")
|
|
615
|
+
async def tools_registry_diagnostics(request: Request):
|
|
616
|
+
"""Small drift check for CI/admin runtime readiness views."""
|
|
617
|
+
require_user(request)
|
|
618
|
+
return {"status": "ok", "diagnostics": tool_registry_diagnostics()}
|
|
605
619
|
|
|
606
620
|
|
|
607
621
|
# ── MCP / skills / plugins router (latticeai.api.mcp, v1.3.0) ────────────────
|
|
@@ -130,6 +130,7 @@ TOOL_GOVERNANCE: Dict[str, ToolPolicy] = {
|
|
|
130
130
|
"todo_read": _r(),
|
|
131
131
|
"local_list": _r(sandbox="home"),
|
|
132
132
|
"local_read": _r(sandbox="home"),
|
|
133
|
+
"read_document": _r(sandbox="home"),
|
|
133
134
|
"git_status": _rs(),
|
|
134
135
|
"git_diff": _rs(),
|
|
135
136
|
"git_log": _rs(),
|
|
@@ -189,6 +190,7 @@ MCP_TOOL_DESCRIPTIONS: Dict[str, str] = {
|
|
|
189
190
|
"clear_history": "Clear chat history to reduce context and speed up responses.",
|
|
190
191
|
"inspect_html": "Inspect local HTML structure and assets.",
|
|
191
192
|
"preview_url": "Return a server URL for a workspace file.",
|
|
193
|
+
"create_web_project": "Create a web project scaffold inside the workspace.",
|
|
192
194
|
"create_docx": "Create a Word DOCX document in the agent workspace.",
|
|
193
195
|
"create_xlsx": "Create an XLSX spreadsheet in the agent workspace.",
|
|
194
196
|
"create_pptx": "Create a PPTX presentation deck in the agent workspace.",
|
|
@@ -251,6 +253,51 @@ class ToolRegistry:
|
|
|
251
253
|
def registered_tools(self) -> frozenset[str]:
|
|
252
254
|
return frozenset(self.handlers)
|
|
253
255
|
|
|
256
|
+
def diagnostics(self) -> Dict[str, Any]:
|
|
257
|
+
"""Return registry drift checks without executing any tool.
|
|
258
|
+
|
|
259
|
+
This is intentionally derived from the live registry instance, not from
|
|
260
|
+
duplicated constants, so admin/runtime surfaces can prove whether the
|
|
261
|
+
dispatch, governance, and catalog projections are still aligned.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
registered = set(self.registered_tools())
|
|
265
|
+
governed = set(self.governance)
|
|
266
|
+
described = set(self.descriptions)
|
|
267
|
+
return {
|
|
268
|
+
"ready": not (governed - registered or registered - governed or registered - described),
|
|
269
|
+
"registered_tools": len(registered),
|
|
270
|
+
"governed_tools": len(governed),
|
|
271
|
+
"described_tools": len(described),
|
|
272
|
+
"governance_without_handler": sorted(governed - registered),
|
|
273
|
+
"handler_without_governance": sorted(registered - governed),
|
|
274
|
+
"handler_without_description": sorted(registered - described),
|
|
275
|
+
"description_without_handler": sorted(described - registered),
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
def manifest(self) -> Dict[str, Any]:
|
|
279
|
+
"""Serializable tool registry contract for runtime/admin inspection."""
|
|
280
|
+
|
|
281
|
+
tools = []
|
|
282
|
+
for name in sorted(self.registered_tools() | set(self.governance) | set(self.descriptions)):
|
|
283
|
+
policy = self.policy_for(name, {})
|
|
284
|
+
tools.append({
|
|
285
|
+
"name": name,
|
|
286
|
+
"registered": name in self.handlers,
|
|
287
|
+
"governed": name in self.governance,
|
|
288
|
+
"described": name in self.descriptions,
|
|
289
|
+
"description": self.descriptions.get(name, ""),
|
|
290
|
+
"policy": dict(policy),
|
|
291
|
+
"permission": dict(self.permission(name, {})),
|
|
292
|
+
})
|
|
293
|
+
diagnostics = self.diagnostics()
|
|
294
|
+
return {
|
|
295
|
+
"status": "ok" if diagnostics["ready"] else "degraded",
|
|
296
|
+
"catalog_brief": self.catalog_brief.strip(),
|
|
297
|
+
"diagnostics": diagnostics,
|
|
298
|
+
"tools": tools,
|
|
299
|
+
}
|
|
300
|
+
|
|
254
301
|
def execute(self, action: str, args: Dict[str, Any], *, error_cls: type[Exception]) -> Dict[str, Any]:
|
|
255
302
|
handler = self.handlers.get(action)
|
|
256
303
|
if handler is None:
|
|
@@ -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 = "7.
|
|
22
|
+
WORKSPACE_OS_VERSION = "7.2.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
|
|
@@ -90,6 +90,12 @@ class ToolDispatchService:
|
|
|
90
90
|
def permissions(self) -> list[ToolPermission]:
|
|
91
91
|
return self.registry.permissions()
|
|
92
92
|
|
|
93
|
+
def diagnostics(self) -> Dict[str, Any]:
|
|
94
|
+
return self.registry.diagnostics()
|
|
95
|
+
|
|
96
|
+
def manifest(self) -> Dict[str, Any]:
|
|
97
|
+
return self.registry.manifest()
|
|
98
|
+
|
|
93
99
|
def check_role(self, tool_name: str, current_user: str) -> None:
|
|
94
100
|
if tool_name not in self.registry.admin_only_tools:
|
|
95
101
|
return
|
|
@@ -131,6 +137,14 @@ def list_tool_permissions() -> list:
|
|
|
131
137
|
return DEFAULT_TOOL_DISPATCH_SERVICE.permissions()
|
|
132
138
|
|
|
133
139
|
|
|
140
|
+
def tool_registry_diagnostics() -> Dict[str, Any]:
|
|
141
|
+
return DEFAULT_TOOL_DISPATCH_SERVICE.diagnostics()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def tool_registry_manifest() -> Dict[str, Any]:
|
|
145
|
+
return DEFAULT_TOOL_DISPATCH_SERVICE.manifest()
|
|
146
|
+
|
|
147
|
+
|
|
134
148
|
def check_tool_role(tool_name: str, current_user: str) -> None:
|
|
135
149
|
DEFAULT_TOOL_DISPATCH_SERVICE.check_role(tool_name, current_user)
|
|
136
150
|
|
package/package.json
CHANGED
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED