ltcai 11.2.0 → 11.4.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 +46 -53
- package/docs/CHANGELOG.md +61 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/MULTI_AGENT_RUNTIME.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -2
- package/docs/PERMISSION_MODE.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +2 -2
- package/docs/v11.3.0_PLAN.md +202 -0
- package/docs/v11.4.0_RUST_FOUNDATION_PLAN.md +176 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_common/__init__.py +287 -0
- package/lattice_brain/graph/_kg_common/extraction.py +516 -0
- package/lattice_brain/graph/_kg_common/relations.py +161 -0
- package/lattice_brain/graph/_kg_common/text.py +479 -0
- package/lattice_brain/graph/discovery_index/__init__.py +35 -0
- package/lattice_brain/graph/discovery_index/cleanup.py +182 -0
- package/lattice_brain/graph/discovery_index/extract.py +137 -0
- package/lattice_brain/graph/discovery_index/scan.py +411 -0
- package/lattice_brain/graph/discovery_index/upsert.py +495 -0
- package/lattice_brain/graph/projection/__init__.py +42 -0
- package/lattice_brain/graph/projection/curation.py +500 -0
- package/lattice_brain/graph/{projection.py → projection/v2_schema.py} +15 -477
- package/lattice_brain/graph/retrieval/__init__.py +54 -0
- package/lattice_brain/graph/retrieval/context.py +197 -0
- package/lattice_brain/graph/retrieval/graph_view.py +319 -0
- package/lattice_brain/graph/retrieval/hybrid.py +488 -0
- package/lattice_brain/graph/retrieval/maintenance.py +121 -0
- package/lattice_brain/graph/retrieval/signals.py +95 -0
- package/lattice_brain/graph/retrieval_vector/__init__.py +42 -0
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +97 -0
- package/lattice_brain/graph/retrieval_vector/indexing.py +347 -0
- package/lattice_brain/graph/retrieval_vector/search.py +560 -0
- package/lattice_brain/graph/retrieval_vector/status.py +374 -0
- package/lattice_brain/ingestion/__init__.py +130 -0
- package/lattice_brain/ingestion/_contract.py +90 -0
- package/lattice_brain/ingestion/constants.py +127 -0
- package/lattice_brain/ingestion/folder_scan.py +57 -0
- package/lattice_brain/ingestion/folders.py +258 -0
- package/lattice_brain/ingestion/hashing.py +26 -0
- package/lattice_brain/ingestion/jobs_api.py +107 -0
- package/lattice_brain/ingestion/models.py +80 -0
- package/lattice_brain/ingestion/pipeline.py +486 -0
- package/lattice_brain/ingestion/quality.py +209 -0
- package/lattice_brain/ingestion/routing.py +295 -0
- package/lattice_brain/multimodal/__init__.py +164 -0
- package/lattice_brain/multimodal/audio.py +77 -0
- package/lattice_brain/multimodal/common.py +118 -0
- package/lattice_brain/multimodal/images.py +498 -0
- package/lattice_brain/multimodal/ports.py +169 -0
- package/lattice_brain/multimodal/video.py +410 -0
- package/lattice_brain/portability/__init__.py +90 -0
- package/lattice_brain/portability/_contract.py +42 -0
- package/lattice_brain/portability/backups.py +338 -0
- package/lattice_brain/portability/bundles.py +136 -0
- package/lattice_brain/portability/constants.py +93 -0
- package/lattice_brain/portability/fsops.py +138 -0
- package/lattice_brain/portability/service.py +41 -0
- package/lattice_brain/{portability.py → portability/sharing.py} +44 -677
- package/lattice_brain/runtime/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chronicle.py +63 -0
- package/latticeai/core/agent/__init__.py +93 -0
- package/latticeai/core/agent/_contract.py +79 -0
- package/latticeai/core/agent/context.py +57 -0
- package/latticeai/core/agent/deps.py +125 -0
- package/latticeai/core/agent/execution.py +622 -0
- package/latticeai/core/agent/planning.py +145 -0
- package/latticeai/core/agent/recovery.py +157 -0
- package/latticeai/core/agent/runtime.py +210 -0
- package/latticeai/core/agent/verification.py +231 -0
- package/latticeai/core/embedding_providers/__init__.py +151 -0
- package/latticeai/core/embedding_providers/base.py +199 -0
- package/latticeai/core/embedding_providers/captions.py +162 -0
- package/latticeai/core/embedding_providers/profiles.py +126 -0
- package/latticeai/core/embedding_providers/text.py +350 -0
- package/latticeai/core/embedding_providers/vision.py +352 -0
- package/latticeai/core/file_generation/__init__.py +115 -0
- package/latticeai/core/file_generation/bundles.py +76 -0
- package/latticeai/core/file_generation/extraction.py +154 -0
- package/latticeai/core/file_generation/inference.py +235 -0
- package/latticeai/core/file_generation/orchestration.py +152 -0
- package/latticeai/core/file_generation/prompting.py +117 -0
- package/latticeai/core/file_generation/repair.py +114 -0
- package/latticeai/core/file_generation/sanitize.py +61 -0
- package/latticeai/core/file_generation/validation.py +201 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +9 -0
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/integrations/telegram_bot/__init__.py +123 -0
- package/latticeai/integrations/telegram_bot/__main__.py +17 -0
- package/latticeai/integrations/telegram_bot/config.py +86 -0
- package/latticeai/integrations/telegram_bot/dispatch.py +311 -0
- package/latticeai/integrations/telegram_bot/flows.py +478 -0
- package/latticeai/integrations/telegram_bot/helpers.py +322 -0
- package/latticeai/integrations/telegram_bot/screens.py +394 -0
- package/latticeai/models/router/__init__.py +88 -0
- package/latticeai/models/router/_contract.py +66 -0
- package/latticeai/models/router/branding.py +56 -0
- package/latticeai/models/router/catalog.py +69 -0
- package/latticeai/models/router/documents.py +199 -0
- package/latticeai/models/router/errors.py +37 -0
- package/latticeai/models/router/generation.py +258 -0
- package/latticeai/models/router/loading.py +291 -0
- package/latticeai/models/router/local_models.py +85 -0
- package/latticeai/models/router/registry.py +147 -0
- package/latticeai/runtime/build_phases/__init__.py +82 -0
- package/latticeai/runtime/build_phases/features.py +407 -0
- package/latticeai/runtime/build_phases/foundation.py +555 -0
- package/latticeai/runtime/build_phases/web.py +492 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/services/architecture_readiness.py +48 -19
- package/latticeai/services/brain_intelligence/__init__.py +58 -0
- package/latticeai/services/brain_intelligence/_contract.py +71 -0
- package/latticeai/services/brain_intelligence/consistency.py +193 -0
- package/latticeai/services/brain_intelligence/constants.py +47 -0
- package/latticeai/services/brain_intelligence/digest.py +258 -0
- package/latticeai/services/brain_intelligence/health.py +331 -0
- package/latticeai/services/brain_intelligence/proposals.py +264 -0
- package/latticeai/services/brain_intelligence/sampling.py +84 -0
- package/latticeai/services/brain_intelligence/service.py +48 -0
- package/latticeai/services/chronicle.py +557 -0
- package/latticeai/services/memory_service/__init__.py +52 -0
- package/latticeai/services/memory_service/_contract.py +100 -0
- package/latticeai/services/memory_service/brief.py +431 -0
- package/latticeai/services/memory_service/constants.py +57 -0
- package/latticeai/services/memory_service/maintenance.py +138 -0
- package/latticeai/services/memory_service/manager.py +186 -0
- package/latticeai/services/memory_service/proof.py +136 -0
- package/latticeai/services/memory_service/recall.py +225 -0
- package/latticeai/services/memory_service/service.py +48 -0
- package/latticeai/services/memory_service/stores.py +110 -0
- package/latticeai/services/model_runtime/__init__.py +322 -0
- package/latticeai/services/model_runtime/cloud.py +87 -0
- package/latticeai/services/model_runtime/download.py +282 -0
- package/latticeai/services/model_runtime/engines.py +341 -0
- package/latticeai/services/model_runtime/loading.py +178 -0
- package/latticeai/services/model_runtime/service.py +129 -0
- package/latticeai/services/model_runtime/state.py +131 -0
- package/latticeai/services/model_runtime/status.py +255 -0
- package/latticeai/services/product_readiness.py +15 -7
- package/latticeai/setup/wizard/__init__.py +126 -0
- package/latticeai/setup/wizard/catalog.py +172 -0
- package/latticeai/setup/wizard/detect.py +323 -0
- package/latticeai/setup/wizard/install.py +348 -0
- package/latticeai/setup/wizard/paths.py +168 -0
- package/latticeai/setup/wizard/plans.py +74 -0
- package/latticeai/setup/wizard/recommend.py +320 -0
- package/package.json +6 -2
- package/scripts/bump_version.py +14 -0
- package/scripts/capture_release_evidence.mjs +33 -21
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_i18n_namespace_coverage.mjs +41 -4
- package/scripts/check_max_file_lines.mjs +102 -0
- package/scripts/check_release_evidence_bound.mjs +30 -15
- package/scripts/check_screenshot_pixel_delta.py +34 -4
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/generate_rust_parity_fixtures.py +562 -0
- package/scripts/lib/mock_server_fingerprint.mjs +94 -0
- package/scripts/release_screen_claims.json +31 -2
- package/src-tauri/Cargo.lock +361 -3
- package/src-tauri/Cargo.toml +6 -1
- package/src-tauri/src/backend.rs +349 -0
- package/src-tauri/src/folder.rs +33 -0
- package/src-tauri/src/main.rs +97 -399
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +41 -37
- package/static/app/assets/Act-yYpYnn0v.js +1 -0
- package/static/app/assets/AdminConsole-DL3Cr5pL.js +1 -0
- package/static/app/assets/{Brain-tuhI4sOC.js → Brain-C1HBN0Wf.js} +2 -2
- package/static/app/assets/BrainHome-DoXRhUUC.js +2 -0
- package/static/app/assets/BrainSignals-6yR6ir5t.js +1 -0
- package/static/app/assets/Capture-CFIRsFNE.js +1 -0
- package/static/app/assets/Chronicle-BZbEgiwN.js +1 -0
- package/static/app/assets/CommandPalette-D2pMxC2I.js +1 -0
- package/static/app/assets/Library-DwO3yZST.js +1 -0
- package/static/app/assets/{LivingBrain-DBwhto14.js → LivingBrain-Jn1GK0-S.js} +1 -1
- package/static/app/assets/ProductFlow-B-w1R4Oo.js +1 -0
- package/static/app/assets/ReviewCard-6B27X8Vg.js +3 -0
- package/static/app/assets/System-DW8F-2xL.js +1 -0
- package/static/app/assets/arrow-left-DXvKg9U6.js +1 -0
- package/static/app/assets/{bot-Cia42c2h.js → bot-IM_E_Y12.js} +1 -1
- package/static/app/assets/brain-Ci1CkWjM.js +1 -0
- package/static/app/assets/{button-2j2Ijzgq.js → button-COwyqfHM.js} +1 -1
- package/static/app/assets/circle-check-DfInj-qD.js +1 -0
- package/static/app/assets/{circle-pause-BEFeWpVW.js → circle-pause-DEM4A1Y5.js} +1 -1
- package/static/app/assets/{circle-play-ujXMcHxl.js → circle-play-C9djDuLd.js} +1 -1
- package/static/app/assets/{cpu-k4awryFq.js → cpu-DFdo1gw-.js} +1 -1
- package/static/app/assets/{download-DFbLJ_ig.js → download-SnJL6oqk.js} +1 -1
- package/static/app/assets/{folder-open-7y_b6xkM.js → folder-open-CqZeDkjE.js} +1 -1
- package/static/app/assets/{hard-drive-Bidh02Kr.js → hard-drive-j1jJXYYf.js} +1 -1
- package/static/app/assets/{index-DwDl9-8Y.css → index-BLPb5lmE.css} +1 -1
- package/static/app/assets/index-_u5iUHDr.js +10 -0
- package/static/app/assets/input-B0lPdRQZ.js +1 -0
- package/static/app/assets/link-2-CoFbooHS.js +1 -0
- package/static/app/assets/{permissionCopy-Bpb83Hx9.js → permissionCopy-BsyLxtao.js} +1 -1
- package/static/app/assets/primitives-DEbN-d6p.js +1 -0
- package/static/app/assets/search-BybIWPNd.js +1 -0
- package/static/app/assets/{share-2-BH1M-WNi.js → share-2-CVtZ_ewX.js} +1 -1
- package/static/app/assets/{shield-alert-BlKdBXcG.js → shield-alert-CBi2GNWM.js} +1 -1
- package/static/app/assets/{textarea-CCWbUfFB.js → textarea-DNMpB5ih.js} +1 -1
- package/static/app/assets/{useFocusTrap-YdHQ7pJ1.js → useFocusTrap-C83t3GXF.js} +1 -1
- package/static/app/assets/useMutation-DtbJDoyz.js +1 -0
- package/static/app/assets/{useQuery-CXQiwbVT.js → useQuery-Dcp1OChy.js} +1 -1
- package/static/app/assets/utils-BlZr7Pd4.js +4 -0
- package/static/app/assets/workspace-jJY4RuAV.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/graph/_kg_common.py +0 -1331
- package/lattice_brain/graph/discovery_index.py +0 -1141
- package/lattice_brain/graph/retrieval.py +0 -1120
- package/lattice_brain/graph/retrieval_vector.py +0 -1293
- package/lattice_brain/ingestion.py +0 -1525
- package/lattice_brain/multimodal.py +0 -1258
- package/latticeai/core/agent.py +0 -1465
- package/latticeai/core/embedding_providers.py +0 -1196
- package/latticeai/core/file_generation.py +0 -1047
- package/latticeai/integrations/telegram_bot.py +0 -1390
- package/latticeai/models/router.py +0 -1007
- package/latticeai/runtime/build_phases.py +0 -1450
- package/latticeai/services/brain_intelligence.py +0 -1083
- package/latticeai/services/memory_service.py +0 -1177
- package/latticeai/services/model_runtime.py +0 -1281
- package/latticeai/setup/wizard.py +0 -1310
- package/static/app/assets/Act-AWf0SAKp.js +0 -1
- package/static/app/assets/AdminConsole-D0u8Tiyj.js +0 -1
- package/static/app/assets/BrainHome-Ts7G_Ila.js +0 -2
- package/static/app/assets/BrainSignals-jMYgQ2Ar.js +0 -1
- package/static/app/assets/Capture-CqOSzyPr.js +0 -1
- package/static/app/assets/CommandPalette-DC0Bzh-I.js +0 -1
- package/static/app/assets/Library-CX-bbhmK.js +0 -1
- package/static/app/assets/ProductFlow-BHA2cfKI.js +0 -1
- package/static/app/assets/ReviewCard-BUhCKRNM.js +0 -3
- package/static/app/assets/System-Bu2t5hn1.js +0 -1
- package/static/app/assets/arrow-left-Dzwa5zRb.js +0 -1
- package/static/app/assets/brain-DJMoqrwx.js +0 -1
- package/static/app/assets/index-BpYkzcVm.js +0 -10
- package/static/app/assets/input-DSlJJxRs.js +0 -1
- package/static/app/assets/primitives-BCx6TvfG.js +0 -1
- package/static/app/assets/search-Cgy8cCFJ.js +0 -1
- package/static/app/assets/utils-zqPZJxdx.js +0 -4
- package/static/app/assets/workspace-DXTihhfU.js +0 -1
|
@@ -44,7 +44,7 @@ orchestrator, and the agent runtime service. Lazy-loaded so importing
|
|
|
44
44
|
/agents/api/runtime/* , POST /agents (start via runtime.start or executor)
|
|
45
45
|
api/chat.py
|
|
46
46
|
from latticeai.services.tool_dispatch import build_agent_runtime
|
|
47
|
-
-> latticeai/core/agent
|
|
47
|
+
-> latticeai/core/agent/:SingleAgentRuntime (별도 state/plan/transcript 머신, single-agent /agent 경로. dispatch_tool 만 공유)
|
|
48
48
|
latticeai/services/tool_dispatch.py:14
|
|
49
49
|
from latticeai.core.agent import SingleAgentRuntime
|
|
50
50
|
(tool governance + core single-agent용)
|
|
@@ -49,7 +49,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
49
49
|
from ..utils import now_iso as _now
|
|
50
50
|
from .contracts import multi_agent_contract
|
|
51
51
|
|
|
52
|
-
MULTI_AGENT_VERSION = "11.
|
|
52
|
+
MULTI_AGENT_VERSION = "11.4.0"
|
|
53
53
|
|
|
54
54
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
55
55
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Brain Chronicle API router (v11.3.0, track B).
|
|
2
|
+
|
|
3
|
+
Read-only surface over :class:`~latticeai.services.chronicle.ChronicleService`
|
|
4
|
+
— the first HTTP exposure of the bitemporal data v11.1.0 started recording:
|
|
5
|
+
|
|
6
|
+
* ``GET /api/chronicle/overview`` — the growth curve: totals, the first and
|
|
7
|
+
last thing the Brain ever saw, and one bucket per day.
|
|
8
|
+
* ``GET /api/chronicle/day/{date}`` — one day's story, grouped into what came
|
|
9
|
+
in, what was learned, what was talked about, and what changed.
|
|
10
|
+
* ``GET /api/chronicle/as-of?ts=…`` — the Brain as it stood at an instant.
|
|
11
|
+
|
|
12
|
+
All three follow the same require_user/gate_read contract as the command
|
|
13
|
+
centre, so a workspace member never reads another workspace's timeline. A
|
|
14
|
+
malformed date or timestamp answers 422 through the message catalog rather
|
|
15
|
+
than a 500 or a silently empty day.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import Callable, Optional
|
|
21
|
+
|
|
22
|
+
from fastapi import APIRouter, Query, Request
|
|
23
|
+
|
|
24
|
+
from latticeai.core.messages import http_error, resolve_language
|
|
25
|
+
from latticeai.services.chronicle import ChronicleService
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_chronicle_router(
|
|
29
|
+
*,
|
|
30
|
+
service: ChronicleService,
|
|
31
|
+
require_user: Callable[[Request], str],
|
|
32
|
+
gate_read: Callable[[Request], Optional[str]],
|
|
33
|
+
) -> APIRouter:
|
|
34
|
+
router = APIRouter()
|
|
35
|
+
|
|
36
|
+
@router.get("/api/chronicle/overview")
|
|
37
|
+
async def chronicle_overview(request: Request):
|
|
38
|
+
user = require_user(request)
|
|
39
|
+
scope = gate_read(request)
|
|
40
|
+
return service.overview(user_email=user, workspace_id=scope)
|
|
41
|
+
|
|
42
|
+
@router.get("/api/chronicle/day/{date}")
|
|
43
|
+
async def chronicle_day(date: str, request: Request):
|
|
44
|
+
user = require_user(request)
|
|
45
|
+
scope = gate_read(request)
|
|
46
|
+
try:
|
|
47
|
+
return service.day(date, user_email=user, workspace_id=scope)
|
|
48
|
+
except ValueError:
|
|
49
|
+
raise http_error(422, "chronicle.bad_date", resolve_language(request))
|
|
50
|
+
|
|
51
|
+
@router.get("/api/chronicle/as-of")
|
|
52
|
+
async def chronicle_as_of(request: Request, ts: str = Query(..., max_length=64)):
|
|
53
|
+
require_user(request)
|
|
54
|
+
scope = gate_read(request)
|
|
55
|
+
try:
|
|
56
|
+
return service.as_of(ts, workspace_id=scope)
|
|
57
|
+
except ValueError:
|
|
58
|
+
raise http_error(422, "chronicle.bad_timestamp", resolve_language(request))
|
|
59
|
+
|
|
60
|
+
return router
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
__all__ = ["create_chronicle_router"]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Single-agent runtime — the Discover→Plan→Implement→Verify state machine.
|
|
2
|
+
|
|
3
|
+
This package is the deep single-agent loop: a small interface (``AgentDeps``
|
|
4
|
+
ports + ``SingleAgentRuntime.run_to_completion``) over the whole role-phased
|
|
5
|
+
state machine (planner → executor → critic → rollback → memory). It carries no
|
|
6
|
+
FastAPI, no globals, and no I/O of its own — every collaborator is injected
|
|
7
|
+
through ``AgentDeps``.
|
|
8
|
+
|
|
9
|
+
Two adapters justify the seam:
|
|
10
|
+
|
|
11
|
+
* production wires ``AgentDeps`` from ``latticeai.server_app``'s ``LLMRouter``, governance
|
|
12
|
+
map, audit log, and prompts;
|
|
13
|
+
* tests pass fake ports (an LLM that returns canned JSON, a recording tool
|
|
14
|
+
executor) and drive a full PLAN→EXECUTE→VERIFY→DONE cycle without a server.
|
|
15
|
+
|
|
16
|
+
HTTP concerns — request parsing, chat-history persistence, response shaping,
|
|
17
|
+
scheduling the background memory update — stay in the app layer. This package
|
|
18
|
+
only owns the state machine.
|
|
19
|
+
|
|
20
|
+
v10.0.1 split the pure helpers and the state vocabulary out to sibling modules
|
|
21
|
+
(``agent_state``, ``agent_helpers``, ``agent_trace``, …). v11.3.0 finished the
|
|
22
|
+
job on what was left, turning the module into a package whose submodules are
|
|
23
|
+
one phase each:
|
|
24
|
+
|
|
25
|
+
* :mod:`.context` — ``AgentRunContext``, the state one run carries;
|
|
26
|
+
* :mod:`.deps` — ``AgentDeps``, the ports;
|
|
27
|
+
* :mod:`.planning` / :mod:`.execution` / :mod:`.verification` / :mod:`.recovery`
|
|
28
|
+
— the four phase mixins;
|
|
29
|
+
* :mod:`.runtime` — ``SingleAgentRuntime``, which composes them and drives the
|
|
30
|
+
loop;
|
|
31
|
+
* :mod:`._contract` — the typing-only seam the mixins share.
|
|
32
|
+
|
|
33
|
+
Every name this module exported still resolves from ``latticeai.core.agent``.
|
|
34
|
+
|
|
35
|
+
Stubbing note: a name rebound *here* changes only this module's binding — the
|
|
36
|
+
submodule that calls it holds its own, so a test standing in for a collaborator
|
|
37
|
+
patches the submodule that reads it.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
# The state vocabulary and the pure helpers live in sibling modules so the loop
|
|
43
|
+
# holds only the loop. They are re-exported (see ``__all__``) because callers —
|
|
44
|
+
# the HTTP layer, run_store, the eval harness, and the tests — have always
|
|
45
|
+
# imported them from here, and that contract does not change.
|
|
46
|
+
from latticeai.core.agent_helpers import PhaseBudgets as PhaseBudgets
|
|
47
|
+
from latticeai.core.agent_helpers import TranscriptBudget as TranscriptBudget
|
|
48
|
+
from latticeai.core.agent_helpers import _truncate_strings as _truncate_strings
|
|
49
|
+
from latticeai.core.agent_helpers import artifact_checklist as artifact_checklist
|
|
50
|
+
from latticeai.core.agent_helpers import compact_transcript as compact_transcript
|
|
51
|
+
from latticeai.core.agent_helpers import extract_action as extract_action
|
|
52
|
+
from latticeai.core.agent_helpers import (
|
|
53
|
+
extract_action_details as extract_action_details,
|
|
54
|
+
)
|
|
55
|
+
from latticeai.core.agent_helpers import files_written as files_written
|
|
56
|
+
from latticeai.core.agent_helpers import filter_learnings as filter_learnings
|
|
57
|
+
from latticeai.core.agent_helpers import (
|
|
58
|
+
format_artifact_checklist as format_artifact_checklist,
|
|
59
|
+
)
|
|
60
|
+
from latticeai.core.agent_helpers import (
|
|
61
|
+
format_requirement_coverage as format_requirement_coverage,
|
|
62
|
+
)
|
|
63
|
+
from latticeai.core.agent_helpers import normalize_plan as normalize_plan
|
|
64
|
+
from latticeai.core.agent_helpers import requirement_coverage as requirement_coverage
|
|
65
|
+
from latticeai.core.agent_state import AGENT_TERMINAL_STATES as AGENT_TERMINAL_STATES
|
|
66
|
+
from latticeai.core.agent_state import AgentState as AgentState
|
|
67
|
+
|
|
68
|
+
from .context import AgentRunContext as AgentRunContext
|
|
69
|
+
from .deps import AgentDeps as AgentDeps
|
|
70
|
+
from .runtime import SingleAgentRuntime as SingleAgentRuntime
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
# this module
|
|
74
|
+
"AgentDeps",
|
|
75
|
+
"AgentRunContext",
|
|
76
|
+
"SingleAgentRuntime",
|
|
77
|
+
# re-exported from agent_state
|
|
78
|
+
"AGENT_TERMINAL_STATES",
|
|
79
|
+
"AgentState",
|
|
80
|
+
# re-exported from agent_helpers
|
|
81
|
+
"PhaseBudgets",
|
|
82
|
+
"TranscriptBudget",
|
|
83
|
+
"artifact_checklist",
|
|
84
|
+
"compact_transcript",
|
|
85
|
+
"extract_action",
|
|
86
|
+
"extract_action_details",
|
|
87
|
+
"files_written",
|
|
88
|
+
"filter_learnings",
|
|
89
|
+
"format_artifact_checklist",
|
|
90
|
+
"format_requirement_coverage",
|
|
91
|
+
"normalize_plan",
|
|
92
|
+
"requirement_coverage",
|
|
93
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""The seam the four phase mixins share.
|
|
2
|
+
|
|
3
|
+
``SingleAgentRuntime`` is assembled from the planning, execution, verification
|
|
4
|
+
and recovery mixins. Each of them reads state it does not own and calls back
|
|
5
|
+
into helpers defined on the runtime itself — the point of the split is that
|
|
6
|
+
"how a plan is approved" and "how a tool call is gated" stop sharing a
|
|
7
|
+
1,465-line file, not that they stop sharing ``self``.
|
|
8
|
+
|
|
9
|
+
Typing-only, exactly like :mod:`lattice_brain.ingestion._contract`: the
|
|
10
|
+
declarations below are never the implementation, so the MRO and every method
|
|
11
|
+
resolution stay byte-for-byte what the single-file class had. Adding a
|
|
12
|
+
cross-mixin call without declaring it here is a type error.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any, FrozenSet, Optional
|
|
18
|
+
|
|
19
|
+
from latticeai.core.agent_helpers import PhaseBudgets, TranscriptBudget
|
|
20
|
+
from latticeai.core.agent_profiles import AgentProfile
|
|
21
|
+
from latticeai.core.permission_mode import PermissionMode
|
|
22
|
+
|
|
23
|
+
from .context import AgentRunContext
|
|
24
|
+
from .deps import AgentDeps
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentCore:
|
|
28
|
+
"""What any phase mixin may assume about ``self``.
|
|
29
|
+
|
|
30
|
+
Never instantiated directly — see the module docstring. Members are
|
|
31
|
+
declared, not implemented: the implementation lives in ``runtime.py`` or in
|
|
32
|
+
whichever mixin owns it.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# ── State owned by SingleAgentRuntime.__init__ ───────────────────────────
|
|
36
|
+
deps: AgentDeps
|
|
37
|
+
_env_phase_budgets: Optional[PhaseBudgets]
|
|
38
|
+
_env_transcript_budget: Optional[TranscriptBudget]
|
|
39
|
+
|
|
40
|
+
# ── runtime.py: budgets, resolved from deps or the environment once ──────
|
|
41
|
+
@property
|
|
42
|
+
def phase_budgets(self) -> PhaseBudgets:
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def transcript_budget(self) -> TranscriptBudget:
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
# ── runtime.py: the autonomy dial every gate in one run agrees on ────────
|
|
50
|
+
def resolve_permission_mode(
|
|
51
|
+
self,
|
|
52
|
+
ctx: Optional[AgentRunContext] = None,
|
|
53
|
+
*,
|
|
54
|
+
user_email: Optional[str] = None,
|
|
55
|
+
workspace_id: Optional[str] = None,
|
|
56
|
+
) -> PermissionMode:
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
|
|
59
|
+
# ── runtime.py: change-governance classification helpers ─────────────────
|
|
60
|
+
def _governed_tools(self) -> FrozenSet[str]:
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
def _governed_path_exists(self, name: str, path: str) -> bool:
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
|
|
66
|
+
# ── runtime.py: how hard the loop works to keep this model on contract ───
|
|
67
|
+
def profile_for(self, model_id: Optional[str]) -> AgentProfile:
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
|
|
70
|
+
# ── runtime.py: advisory telemetry every phase emits ─────────────────────
|
|
71
|
+
def _emit_step(
|
|
72
|
+
self, ctx: AgentRunContext, phase: str, event: str, **details: Any
|
|
73
|
+
) -> None:
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
# ── runtime.py: project-session prompt block, or "" for a standalone run ─
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _project_block(ctx: AgentRunContext) -> str:
|
|
79
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""The mutable state one agent run carries from phase to phase.
|
|
2
|
+
|
|
3
|
+
Every phase reads and writes this object and nothing else: the loop itself is
|
|
4
|
+
stateless, so a run is exactly what its :class:`AgentRunContext` says it is.
|
|
5
|
+
``__slots__`` is deliberate — a typo'd attribute is an error, not a silently
|
|
6
|
+
ignored write that a later phase never sees.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Callable, Dict, Optional
|
|
12
|
+
|
|
13
|
+
from latticeai.core.agent_state import AgentState
|
|
14
|
+
from latticeai.core.agent_trace import LoopTrace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AgentRunContext:
|
|
18
|
+
"""Mutable state carrier passed through all agent phases."""
|
|
19
|
+
__slots__ = ("state", "plan", "transcript", "retry_count",
|
|
20
|
+
"state_history", "corrections", "final_message", "rollback_log",
|
|
21
|
+
"executing_model", "reviewing_model", "approved_by_human", "trace",
|
|
22
|
+
"on_step", "project_context", "permission_mode",
|
|
23
|
+
"self_model_summary")
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
self.state: AgentState = AgentState.IDLE
|
|
27
|
+
self.trace: LoopTrace = LoopTrace()
|
|
28
|
+
self.plan: dict = {}
|
|
29
|
+
self.transcript: list = []
|
|
30
|
+
self.retry_count: int = 0
|
|
31
|
+
self.state_history: list = []
|
|
32
|
+
self.corrections: list = []
|
|
33
|
+
self.final_message: str = ""
|
|
34
|
+
self.rollback_log: list = []
|
|
35
|
+
self.executing_model: Optional[str] = None
|
|
36
|
+
self.reviewing_model: Optional[str] = None
|
|
37
|
+
self.approved_by_human: bool = False
|
|
38
|
+
# Per-run step observer (review Wave 1.1): the HTTP layer attaches a
|
|
39
|
+
# callback here so live SSE clients see progress while EXECUTING.
|
|
40
|
+
# Never serialized; a broken observer never breaks the loop.
|
|
41
|
+
self.on_step: Optional[Callable[[Dict[str, Any]], None]] = None
|
|
42
|
+
# Multi-turn project loop (v9.9.6): a prompt block describing where the
|
|
43
|
+
# project stands — files already produced, open TODOs, the last honest
|
|
44
|
+
# verification. Empty for a standalone run, which behaves exactly as
|
|
45
|
+
# before. Set by the HTTP layer, read by plan/execute/verify.
|
|
46
|
+
self.project_context: str = ""
|
|
47
|
+
# Autonomy dial resolved once per run (v9.9.8). The HTTP layer stamps
|
|
48
|
+
# the user/workspace-scoped mode here so the plan gate and every
|
|
49
|
+
# per-tool gate in the same run agree; ``None`` falls back to the
|
|
50
|
+
# process-wide resolver on ``deps``.
|
|
51
|
+
self.permission_mode: Optional[str] = None
|
|
52
|
+
# Self-Model summary resolved once per run (v11.2.0). The executor
|
|
53
|
+
# prompt is rebuilt on every turn of the loop; reading the profile
|
|
54
|
+
# once and reusing it keeps a graph query off that hot path — and
|
|
55
|
+
# keeps every turn of one run describing the same person. ``None``
|
|
56
|
+
# means "not resolved yet"; ``""`` is a resolved, empty profile.
|
|
57
|
+
self.self_model_summary: Optional[str] = None
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""The ports the state machine needs from the outside world.
|
|
2
|
+
|
|
3
|
+
Everything the loop touches is declared here, which is what lets the whole
|
|
4
|
+
PLAN→EXECUTE→VERIFY cycle run against fakes: production wires these from the
|
|
5
|
+
app layer's router, governance map, audit log and prompts; tests pass an LLM
|
|
6
|
+
that returns canned JSON and a recording tool executor.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Awaitable, Callable, Dict, FrozenSet, Mapping, Optional
|
|
14
|
+
|
|
15
|
+
from latticeai.core.agent_helpers import PhaseBudgets, TranscriptBudget
|
|
16
|
+
from latticeai.core.agent_profiles import AgentProfile
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class AgentDeps:
|
|
21
|
+
"""The ports a :class:`SingleAgentRuntime` needs from the outside world.
|
|
22
|
+
|
|
23
|
+
Everything the state machine touches is here, so the loop can be exercised
|
|
24
|
+
against fakes. See module docstring for the two-adapter rationale.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# ── LLM port ─────────────────────────────────────────────────────
|
|
28
|
+
# generate_as(model_id, message, context, max_tokens, temperature) -> str
|
|
29
|
+
generate_as: Callable[..., Awaitable[Any]]
|
|
30
|
+
# generate(message, context, max_tokens, temperature) -> str
|
|
31
|
+
generate: Callable[..., Awaitable[Any]]
|
|
32
|
+
|
|
33
|
+
# ── tool port ────────────────────────────────────────────────────
|
|
34
|
+
execute_tool: Callable[[str, dict], dict]
|
|
35
|
+
policy_for: Callable[[str, dict], Mapping[str, Any]] # name, args -> policy
|
|
36
|
+
risk_level: Callable[[Any], str] # policy -> "low"|"medium"|"high"
|
|
37
|
+
check_role: Callable[[str, str], None] # tool_name, user -> raises if not allowed
|
|
38
|
+
tool_governance: Mapping[str, Mapping[str, Any]] # name -> policy (auto_approve set)
|
|
39
|
+
file_create_actions: FrozenSet[str]
|
|
40
|
+
|
|
41
|
+
# ── context / memory / audit ports ───────────────────────────────
|
|
42
|
+
recent_chat_context: Callable[..., str] # (conversation_id=...) -> str
|
|
43
|
+
clear_history: Callable[[int], dict]
|
|
44
|
+
knowledge_save: Callable[..., Any]
|
|
45
|
+
audit: Callable[..., None] # (event, **kw) -> None
|
|
46
|
+
|
|
47
|
+
# ── prompts + config ─────────────────────────────────────────────
|
|
48
|
+
planner_prompt: str
|
|
49
|
+
executor_prompt: str
|
|
50
|
+
critic_prompt: str
|
|
51
|
+
memory_updater_prompt: str
|
|
52
|
+
agent_root: Path
|
|
53
|
+
|
|
54
|
+
# ── rollback port (optional) ─────────────────────────────────────
|
|
55
|
+
# Production injects this from the tool dispatch service so this pure
|
|
56
|
+
# state machine does not shell out directly. Tests can pass a recorder.
|
|
57
|
+
rollback_file: Optional[Callable[[str], Dict[str, Any]]] = None
|
|
58
|
+
|
|
59
|
+
# ── snapshot rollback ports (optional, review L7) ────────────────
|
|
60
|
+
# git-only rollback left non-git workspaces and newly created files
|
|
61
|
+
# unrecoverable. ``snapshot_file(path)`` captures pre-write state
|
|
62
|
+
# ({"existed", "content", "too_large"}) before a file-create action;
|
|
63
|
+
# ``restore_snapshot(path, content)`` restores it (content=None deletes
|
|
64
|
+
# a file the run created). Both are production-wired with workspace
|
|
65
|
+
# path safety; tests pass recorders.
|
|
66
|
+
snapshot_file: Optional[Callable[[str], Dict[str, Any]]] = None
|
|
67
|
+
restore_snapshot: Optional[Callable[[str, Optional[str]], Dict[str, Any]]] = None
|
|
68
|
+
|
|
69
|
+
# ── lifecycle hooks port (optional) ──────────────────────────────
|
|
70
|
+
# When present, every tool execution fires the shared pre_tool/post_tool
|
|
71
|
+
# lifecycle, so the agent tool path no longer bypasses hooks.
|
|
72
|
+
hooks: Any = None
|
|
73
|
+
|
|
74
|
+
# ── brain memory port (optional) ─────────────────────────────────
|
|
75
|
+
# When present, completed-run learnings become typed Experience records
|
|
76
|
+
# through the unified ingestion pipeline (with provenance), replacing
|
|
77
|
+
# the vault markdown dump.
|
|
78
|
+
brain_memory: Any = None
|
|
79
|
+
|
|
80
|
+
# ── change governor port (optional) ──────────────────────────────
|
|
81
|
+
# When present, file writes are classified centrally: additive creates
|
|
82
|
+
# run with minimal friction, while mutations/deletions of existing
|
|
83
|
+
# content are staged as review proposals instead of applied. The port is
|
|
84
|
+
# ``review(name, args, policy=..., user_email=..., workspace_id=...)``
|
|
85
|
+
# returning None (fall through to the classic gates) or a verdict dict.
|
|
86
|
+
change_governor: Any = None
|
|
87
|
+
|
|
88
|
+
# ── phase budgets (optional) ─────────────────────────────────────
|
|
89
|
+
# Per-phase token caps (plan/execute/verify/memory). None reads the
|
|
90
|
+
# environment once at first use; tests inject a fixed PhaseBudgets.
|
|
91
|
+
phase_budgets: Optional[PhaseBudgets] = None
|
|
92
|
+
|
|
93
|
+
# ── transcript shaping (optional) ────────────────────────────────
|
|
94
|
+
# Executor/critic prompt window caps. None reads the environment once;
|
|
95
|
+
# tests inject a fixed TranscriptBudget.
|
|
96
|
+
transcript_budget: Optional["TranscriptBudget"] = None
|
|
97
|
+
|
|
98
|
+
# ── step observer port (optional) ────────────────────────────────
|
|
99
|
+
# Default per-runtime observer for live step events; a per-run observer
|
|
100
|
+
# can also be attached on AgentRunContext.on_step. Both are advisory.
|
|
101
|
+
on_step: Optional[Callable[[Dict[str, Any]], None]] = None
|
|
102
|
+
|
|
103
|
+
# ── agent profile (optional, v9.9.7) ─────────────────────────────
|
|
104
|
+
# How hard the loop works to keep a weak model on contract. None selects
|
|
105
|
+
# per-run from the executing model id (``profile_for_model``); tests
|
|
106
|
+
# inject a fixed profile.
|
|
107
|
+
agent_profile: Optional["AgentProfile"] = None
|
|
108
|
+
|
|
109
|
+
# ── permission mode port (optional, v9.9.8) ──────────────────────
|
|
110
|
+
# The autonomy dial. Either a static mode, or a resolver callable that
|
|
111
|
+
# accepts ``user_email``/``workspace_id`` scope kwargs (preferred) or no
|
|
112
|
+
# arguments at all — see ``call_mode_source``. ``None`` means strict,
|
|
113
|
+
# which is exactly the pre-9.9.8 behaviour.
|
|
114
|
+
permission_mode: Any = None
|
|
115
|
+
|
|
116
|
+
# ── Self-Model port (optional, v11.2.0) ──────────────────────────
|
|
117
|
+
# What the Brain has learned about its owner, injected into the executor
|
|
118
|
+
# prompt. Either a fixed string or a resolver callable taking
|
|
119
|
+
# ``user_email``/``workspace_id`` scope kwargs (or none at all). 11.1.0
|
|
120
|
+
# built ``executor_prompt_for(self_model_summary=…)`` and then had nothing
|
|
121
|
+
# to pass it, because the runtime held its executor prompt as a fixed
|
|
122
|
+
# string; this is the port that was missing. ``None`` — and an empty
|
|
123
|
+
# summary — produce exactly the prompt bytes the loop produced before it
|
|
124
|
+
# existed.
|
|
125
|
+
self_model_summary: Any = None
|