continuum-toolkit 1.0.0__py3-none-any.whl
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.
- cli/__init__.py +12 -0
- cli/main.py +415 -0
- confidence/__init__.py +13 -0
- confidence/calculator.py +308 -0
- confidence/models.py +40 -0
- context/__init__.py +17 -0
- context/models.py +108 -0
- context/pruner.py +228 -0
- context/selector.py +193 -0
- continuum_toolkit-1.0.0.dist-info/METADATA +511 -0
- continuum_toolkit-1.0.0.dist-info/RECORD +72 -0
- continuum_toolkit-1.0.0.dist-info/WHEEL +5 -0
- continuum_toolkit-1.0.0.dist-info/entry_points.txt +2 -0
- continuum_toolkit-1.0.0.dist-info/licenses/LICENSE +21 -0
- continuum_toolkit-1.0.0.dist-info/top_level.txt +13 -0
- contradictions/__init__.py +19 -0
- contradictions/detector.py +442 -0
- contradictions/models.py +75 -0
- core/__init__.py +97 -0
- core/enums.py +130 -0
- core/evidence.py +117 -0
- core/interfaces.py +209 -0
- core/schema.py +391 -0
- core/serializer.py +84 -0
- core/state_models.py +530 -0
- daemon/__init__.py +12 -0
- daemon/service.py +170 -0
- extractors/__init__.py +51 -0
- extractors/base.py +117 -0
- extractors/config/parsers.py +288 -0
- extractors/config/secret_sanitizer.py +91 -0
- extractors/config_extractor.py +172 -0
- extractors/conversation/analyzers.py +193 -0
- extractors/conversation/models.py +148 -0
- extractors/conversation_extractor.py +166 -0
- extractors/git_extractor.py +305 -0
- extractors/parsers/base.py +91 -0
- extractors/parsers/comment_parser.py +51 -0
- extractors/parsers/js_ts_parser.py +171 -0
- extractors/parsers/python_parser.py +180 -0
- extractors/verification/runners.py +278 -0
- extractors/verification_extractor.py +263 -0
- extractors/workspace_extractor.py +221 -0
- graph/__init__.py +24 -0
- graph/diff.py +109 -0
- graph/manager.py +473 -0
- graph/models.py +62 -0
- graph/propagator.py +194 -0
- graph/query.py +86 -0
- graph/snapshot.py +85 -0
- handoff/__init__.py +28 -0
- handoff/adapters/__init__.py +45 -0
- handoff/adapters/base.py +90 -0
- handoff/adapters/claude_adapter.py +176 -0
- handoff/adapters/codex_gpt_adapter.py +151 -0
- handoff/adapters/gemini_adapter.py +151 -0
- handoff/adapters/local_model_adapter.py +130 -0
- handoff/models.py +88 -0
- handoff/packager.py +288 -0
- pipeline/__init__.py +9 -0
- pipeline/orchestrator.py +260 -0
- resolution/__init__.py +15 -0
- resolution/resolver.py +311 -0
- storage/__init__.py +18 -0
- storage/hooks.py +125 -0
- storage/manager.py +127 -0
- storage/models.py +39 -0
- storage/recovery.py +98 -0
- watcher/__init__.py +17 -0
- watcher/detector.py +136 -0
- watcher/models.py +62 -0
- watcher/updater.py +163 -0
core/state_models.py
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Three-State Models & Canonical State
|
|
3
|
+
========================================================
|
|
4
|
+
Implements the core ontological separation between:
|
|
5
|
+
1. Project State (Physical Ground Truth)
|
|
6
|
+
2. Conversational State (Intent & Reasoning Claims)
|
|
7
|
+
3. Agent Execution State (Tactical In-Flight Intent)
|
|
8
|
+
and the aggregated CanonicalProjectState container.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field, asdict
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
from core.enums import Status, NodeType, RelationType, EvidenceLevel
|
|
17
|
+
from core.evidence import Evidence
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ==============================================================================
|
|
21
|
+
# 1. PROJECT STATE (PHYSICAL GROUND TRUTH)
|
|
22
|
+
# ==============================================================================
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class AstSymbol:
|
|
26
|
+
"""Represents a code symbol extracted from AST (class, function, method, interface)."""
|
|
27
|
+
name: str
|
|
28
|
+
kind: str # function, class, interface, method, variable
|
|
29
|
+
file_path: str
|
|
30
|
+
line_start: int
|
|
31
|
+
line_end: int
|
|
32
|
+
exported: bool = False
|
|
33
|
+
docstring: Optional[str] = None
|
|
34
|
+
parameters: List[str] = field(default_factory=list)
|
|
35
|
+
return_type: Optional[str] = None
|
|
36
|
+
evidence_id: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
39
|
+
return asdict(self)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_dict(cls, data: Dict[str, Any]) -> "AstSymbol":
|
|
43
|
+
return cls(**data)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class TestResult:
|
|
48
|
+
"""Represents a verifiable test execution result."""
|
|
49
|
+
test_id: str
|
|
50
|
+
name: str
|
|
51
|
+
suite: str
|
|
52
|
+
status: Status # VERIFIED (passed) or FAILED
|
|
53
|
+
exit_code: int
|
|
54
|
+
duration_ms: float
|
|
55
|
+
output_snippet: str = ""
|
|
56
|
+
error_message: Optional[str] = None
|
|
57
|
+
evidence_id: Optional[str] = None
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
60
|
+
d = asdict(self)
|
|
61
|
+
d["status"] = self.status.value
|
|
62
|
+
return d
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_dict(cls, data: Dict[str, Any]) -> "TestResult":
|
|
66
|
+
data_copy = dict(data)
|
|
67
|
+
data_copy["status"] = Status(data_copy["status"])
|
|
68
|
+
return cls(**data_copy)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class GitState:
|
|
73
|
+
"""Represents verifiable Git repository state."""
|
|
74
|
+
is_repo: bool = False
|
|
75
|
+
branch: Optional[str] = None
|
|
76
|
+
head_commit: Optional[str] = None
|
|
77
|
+
is_dirty: bool = False
|
|
78
|
+
staged_files: List[str] = field(default_factory=list)
|
|
79
|
+
unstaged_files: List[str] = field(default_factory=list)
|
|
80
|
+
untracked_files: List[str] = field(default_factory=list)
|
|
81
|
+
recent_commits: List[Dict[str, Any]] = field(default_factory=list)
|
|
82
|
+
evidence_ids: List[str] = field(default_factory=list)
|
|
83
|
+
|
|
84
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
85
|
+
return asdict(self)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_dict(cls, data: Dict[str, Any]) -> "GitState":
|
|
89
|
+
return cls(**data)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class ManifestInfo:
|
|
94
|
+
"""Represents project manifests, package definitions, dependencies, and scripts."""
|
|
95
|
+
manifest_type: str # package.json, pyproject.toml, Cargo.toml, etc.
|
|
96
|
+
file_path: str
|
|
97
|
+
project_name: Optional[str] = None
|
|
98
|
+
version: Optional[str] = None
|
|
99
|
+
dependencies: Dict[str, str] = field(default_factory=dict)
|
|
100
|
+
dev_dependencies: Dict[str, str] = field(default_factory=dict)
|
|
101
|
+
scripts: Dict[str, str] = field(default_factory=dict)
|
|
102
|
+
evidence_id: Optional[str] = None
|
|
103
|
+
|
|
104
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
105
|
+
return asdict(self)
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ManifestInfo":
|
|
109
|
+
return cls(**data)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class ProjectState:
|
|
114
|
+
"""
|
|
115
|
+
State 1: Physical Reality of the Project.
|
|
116
|
+
Contains solely physical facts derived from disk, Git, AST, tests, and builds.
|
|
117
|
+
"""
|
|
118
|
+
root_path: str
|
|
119
|
+
detected_languages: List[str] = field(default_factory=list)
|
|
120
|
+
files: List[str] = field(default_factory=list)
|
|
121
|
+
manifests: List[ManifestInfo] = field(default_factory=list)
|
|
122
|
+
symbols: List[AstSymbol] = field(default_factory=list)
|
|
123
|
+
git_state: GitState = field(default_factory=GitState)
|
|
124
|
+
test_results: List[TestResult] = field(default_factory=list)
|
|
125
|
+
build_status: Status = Status.UNKNOWN
|
|
126
|
+
todo_markers: List[Dict[str, Any]] = field(default_factory=list)
|
|
127
|
+
evidence_ids: List[str] = field(default_factory=list)
|
|
128
|
+
last_scanned_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
129
|
+
|
|
130
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
131
|
+
return {
|
|
132
|
+
"root_path": self.root_path,
|
|
133
|
+
"detected_languages": self.detected_languages,
|
|
134
|
+
"files": self.files,
|
|
135
|
+
"manifests": [m.to_dict() for m in self.manifests],
|
|
136
|
+
"symbols": [s.to_dict() for s in self.symbols],
|
|
137
|
+
"git_state": self.git_state.to_dict(),
|
|
138
|
+
"test_results": [t.to_dict() for t in self.test_results],
|
|
139
|
+
"build_status": self.build_status.value,
|
|
140
|
+
"todo_markers": self.todo_markers,
|
|
141
|
+
"evidence_ids": self.evidence_ids,
|
|
142
|
+
"last_scanned_at": self.last_scanned_at
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ProjectState":
|
|
147
|
+
return cls(
|
|
148
|
+
root_path=data.get("root_path", ""),
|
|
149
|
+
detected_languages=data.get("detected_languages", []),
|
|
150
|
+
files=data.get("files", []),
|
|
151
|
+
manifests=[ManifestInfo.from_dict(m) for m in data.get("manifests", [])],
|
|
152
|
+
symbols=[AstSymbol.from_dict(s) for s in data.get("symbols", [])],
|
|
153
|
+
git_state=GitState.from_dict(data.get("git_state", {})),
|
|
154
|
+
test_results=[TestResult.from_dict(t) for t in data.get("test_results", [])],
|
|
155
|
+
build_status=Status(data.get("build_status", Status.UNKNOWN.value)),
|
|
156
|
+
todo_markers=data.get("todo_markers", []),
|
|
157
|
+
evidence_ids=data.get("evidence_ids", []),
|
|
158
|
+
last_scanned_at=data.get("last_scanned_at", datetime.now(timezone.utc).isoformat())
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ==============================================================================
|
|
163
|
+
# 2. CONVERSATIONAL STATE (INTENT & REASONING CLAIMS)
|
|
164
|
+
# ==============================================================================
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class Requirement:
|
|
168
|
+
id: str
|
|
169
|
+
title: str
|
|
170
|
+
description: str
|
|
171
|
+
source_turn: Optional[int] = None
|
|
172
|
+
status: Status = Status.PENDING
|
|
173
|
+
evidence_id: Optional[str] = None
|
|
174
|
+
|
|
175
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
176
|
+
d = asdict(self)
|
|
177
|
+
d["status"] = self.status.value
|
|
178
|
+
return d
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Requirement":
|
|
182
|
+
d = dict(data)
|
|
183
|
+
d["status"] = Status(d["status"])
|
|
184
|
+
return cls(**d)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class ArchitecturalDecision:
|
|
189
|
+
id: str
|
|
190
|
+
title: str
|
|
191
|
+
rationale: str
|
|
192
|
+
constraints: List[str] = field(default_factory=list)
|
|
193
|
+
recorded_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
194
|
+
evidence_id: Optional[str] = None
|
|
195
|
+
|
|
196
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
197
|
+
return asdict(self)
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ArchitecturalDecision":
|
|
201
|
+
return cls(**data)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@dataclass
|
|
205
|
+
class AgentClaim:
|
|
206
|
+
"""An unverified claim made by an AI model in chat (e.g. 'Payment service complete')."""
|
|
207
|
+
id: str
|
|
208
|
+
claim_text: str
|
|
209
|
+
target_component: Optional[str] = None
|
|
210
|
+
claimed_status: Status = Status.VERIFIED
|
|
211
|
+
source_agent: str = "unknown_agent"
|
|
212
|
+
turn_id: Optional[int] = None
|
|
213
|
+
confidence_claimed: Optional[float] = None
|
|
214
|
+
evidence_id: Optional[str] = None
|
|
215
|
+
|
|
216
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
217
|
+
d = asdict(self)
|
|
218
|
+
d["claimed_status"] = self.claimed_status.value
|
|
219
|
+
return d
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def from_dict(cls, data: Dict[str, Any]) -> "AgentClaim":
|
|
223
|
+
d = dict(data)
|
|
224
|
+
d["claimed_status"] = Status(d["claimed_status"])
|
|
225
|
+
return cls(**d)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@dataclass
|
|
229
|
+
class UnresolvedQuestion:
|
|
230
|
+
id: str
|
|
231
|
+
question: str
|
|
232
|
+
context: str = ""
|
|
233
|
+
blocking: bool = False
|
|
234
|
+
asked_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
235
|
+
evidence_id: Optional[str] = None
|
|
236
|
+
|
|
237
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
238
|
+
return asdict(self)
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def from_dict(cls, data: Dict[str, Any]) -> "UnresolvedQuestion":
|
|
242
|
+
return cls(**data)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class ConversationalState:
|
|
247
|
+
"""
|
|
248
|
+
State 2: Intent, Decisions, and Claims from conversations.
|
|
249
|
+
Kept strictly segregated from physical reality.
|
|
250
|
+
"""
|
|
251
|
+
session_id: str = field(default_factory=lambda: f"sess_{uuid.uuid4().hex[:8]}")
|
|
252
|
+
user_requirements: List[Requirement] = field(default_factory=list)
|
|
253
|
+
architectural_decisions: List[ArchitecturalDecision] = field(default_factory=list)
|
|
254
|
+
agent_claims: List[AgentClaim] = field(default_factory=list)
|
|
255
|
+
assumptions: List[str] = field(default_factory=list)
|
|
256
|
+
unresolved_questions: List[UnresolvedQuestion] = field(default_factory=list)
|
|
257
|
+
transcript_provenance: List[str] = field(default_factory=list)
|
|
258
|
+
|
|
259
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
260
|
+
return {
|
|
261
|
+
"session_id": self.session_id,
|
|
262
|
+
"user_requirements": [r.to_dict() for r in self.user_requirements],
|
|
263
|
+
"architectural_decisions": [d.to_dict() for d in self.architectural_decisions],
|
|
264
|
+
"agent_claims": [c.to_dict() for c in self.agent_claims],
|
|
265
|
+
"assumptions": self.assumptions,
|
|
266
|
+
"unresolved_questions": [q.to_dict() for q in self.unresolved_questions],
|
|
267
|
+
"transcript_provenance": self.transcript_provenance
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@classmethod
|
|
271
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ConversationalState":
|
|
272
|
+
return cls(
|
|
273
|
+
session_id=data.get("session_id", f"sess_{uuid.uuid4().hex[:8]}"),
|
|
274
|
+
user_requirements=[Requirement.from_dict(r) for r in data.get("user_requirements", [])],
|
|
275
|
+
architectural_decisions=[ArchitecturalDecision.from_dict(d) for d in data.get("architectural_decisions", [])],
|
|
276
|
+
agent_claims=[AgentClaim.from_dict(c) for c in data.get("agent_claims", [])],
|
|
277
|
+
assumptions=data.get("assumptions", []),
|
|
278
|
+
unresolved_questions=[UnresolvedQuestion.from_dict(q) for q in data.get("unresolved_questions", [])],
|
|
279
|
+
transcript_provenance=data.get("transcript_provenance", [])
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# ==============================================================================
|
|
284
|
+
# 3. AGENT EXECUTION STATE (TACTICAL IN-FLIGHT INTENT)
|
|
285
|
+
# ==============================================================================
|
|
286
|
+
|
|
287
|
+
@dataclass
|
|
288
|
+
class TacticalTask:
|
|
289
|
+
id: str
|
|
290
|
+
title: str
|
|
291
|
+
status: Status = Status.IN_PROGRESS
|
|
292
|
+
target_files: List[str] = field(default_factory=list)
|
|
293
|
+
target_symbols: List[str] = field(default_factory=list)
|
|
294
|
+
notes: str = ""
|
|
295
|
+
|
|
296
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
297
|
+
d = asdict(self)
|
|
298
|
+
d["status"] = self.status.value
|
|
299
|
+
return d
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
def from_dict(cls, data: Dict[str, Any]) -> "TacticalTask":
|
|
303
|
+
d = dict(data)
|
|
304
|
+
d["status"] = Status(d["status"])
|
|
305
|
+
return cls(**d)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@dataclass
|
|
309
|
+
class NextActionRecommendation:
|
|
310
|
+
action_type: str # e.g., "EDIT_FILE", "RUN_TEST", "FIX_SYNTAX", "RESOLVE_CONTRADICTION"
|
|
311
|
+
target_uri: str
|
|
312
|
+
description: str
|
|
313
|
+
prerequisites: List[str] = field(default_factory=list)
|
|
314
|
+
|
|
315
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
316
|
+
return asdict(self)
|
|
317
|
+
|
|
318
|
+
@classmethod
|
|
319
|
+
def from_dict(cls, data: Dict[str, Any]) -> "NextActionRecommendation":
|
|
320
|
+
return cls(**data)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@dataclass
|
|
324
|
+
class AgentExecutionState:
|
|
325
|
+
"""
|
|
326
|
+
State 3: What the previous AI was in the middle of executing.
|
|
327
|
+
Tracks in-flight refactorings, modified files, transient errors, and next actions.
|
|
328
|
+
"""
|
|
329
|
+
agent_id: str = "agent_unknown"
|
|
330
|
+
model_name: str = "unknown"
|
|
331
|
+
active_tasks: List[TacticalTask] = field(default_factory=list)
|
|
332
|
+
modified_files_in_flight: List[str] = field(default_factory=list)
|
|
333
|
+
last_error_encountered: Optional[str] = None
|
|
334
|
+
next_action: Optional[NextActionRecommendation] = None
|
|
335
|
+
execution_context_metadata: Dict[str, Any] = field(default_factory=dict)
|
|
336
|
+
updated_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
337
|
+
|
|
338
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
339
|
+
return {
|
|
340
|
+
"agent_id": self.agent_id,
|
|
341
|
+
"model_name": self.model_name,
|
|
342
|
+
"active_tasks": [t.to_dict() for t in self.active_tasks],
|
|
343
|
+
"modified_files_in_flight": self.modified_files_in_flight,
|
|
344
|
+
"last_error_encountered": self.last_error_encountered,
|
|
345
|
+
"next_action": self.next_action.to_dict() if self.next_action else None,
|
|
346
|
+
"execution_context_metadata": self.execution_context_metadata,
|
|
347
|
+
"updated_at": self.updated_at
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@classmethod
|
|
351
|
+
def from_dict(cls, data: Dict[str, Any]) -> "AgentExecutionState":
|
|
352
|
+
next_act = NextActionRecommendation.from_dict(data["next_action"]) if data.get("next_action") else None
|
|
353
|
+
return cls(
|
|
354
|
+
agent_id=data.get("agent_id", "agent_unknown"),
|
|
355
|
+
model_name=data.get("model_name", "unknown"),
|
|
356
|
+
active_tasks=[TacticalTask.from_dict(t) for t in data.get("active_tasks", [])],
|
|
357
|
+
modified_files_in_flight=data.get("modified_files_in_flight", []),
|
|
358
|
+
last_error_encountered=data.get("last_error_encountered"),
|
|
359
|
+
next_action=next_act,
|
|
360
|
+
execution_context_metadata=data.get("execution_context_metadata", {}),
|
|
361
|
+
updated_at=data.get("updated_at", datetime.now(timezone.utc).isoformat())
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
# ==============================================================================
|
|
366
|
+
# CANONICAL GRAPH NODES & CONTRADICTION MODELS
|
|
367
|
+
# ==============================================================================
|
|
368
|
+
|
|
369
|
+
@dataclass
|
|
370
|
+
class ContradictionRecord:
|
|
371
|
+
"""
|
|
372
|
+
Represents an explicit discrepancy between claims/docs and physical reality.
|
|
373
|
+
"""
|
|
374
|
+
id: str
|
|
375
|
+
severity: str # HIGH, MEDIUM, LOW
|
|
376
|
+
claim_id: Optional[str] = None
|
|
377
|
+
claim_text: str = ""
|
|
378
|
+
physical_evidence_id: Optional[str] = None
|
|
379
|
+
explanation: str = ""
|
|
380
|
+
detected_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
381
|
+
resolved: bool = False
|
|
382
|
+
|
|
383
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
384
|
+
return asdict(self)
|
|
385
|
+
|
|
386
|
+
@classmethod
|
|
387
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ContradictionRecord":
|
|
388
|
+
return cls(**data)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@dataclass
|
|
392
|
+
class GraphNode:
|
|
393
|
+
"""Node in the Canonical State Graph (DAG)."""
|
|
394
|
+
id: str
|
|
395
|
+
name: str
|
|
396
|
+
node_type: NodeType
|
|
397
|
+
status: Status = Status.UNKNOWN
|
|
398
|
+
confidence_score: float = 0.0 # 0.0 to 100.0
|
|
399
|
+
evidence_ids: List[str] = field(default_factory=list)
|
|
400
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
401
|
+
|
|
402
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
403
|
+
return {
|
|
404
|
+
"id": self.id,
|
|
405
|
+
"name": self.name,
|
|
406
|
+
"node_type": self.node_type.value,
|
|
407
|
+
"status": self.status.value,
|
|
408
|
+
"confidence_score": self.confidence_score,
|
|
409
|
+
"evidence_ids": self.evidence_ids,
|
|
410
|
+
"metadata": self.metadata
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@classmethod
|
|
414
|
+
def from_dict(cls, data: Dict[str, Any]) -> "GraphNode":
|
|
415
|
+
return cls(
|
|
416
|
+
id=data["id"],
|
|
417
|
+
name=data["name"],
|
|
418
|
+
node_type=NodeType(data["node_type"]),
|
|
419
|
+
status=Status(data.get("status", Status.UNKNOWN.value)),
|
|
420
|
+
confidence_score=float(data.get("confidence_score", 0.0)),
|
|
421
|
+
evidence_ids=data.get("evidence_ids", []),
|
|
422
|
+
metadata=data.get("metadata", {})
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@dataclass
|
|
427
|
+
class GraphEdge:
|
|
428
|
+
"""Directed edge in the Canonical State Graph."""
|
|
429
|
+
source_id: str
|
|
430
|
+
target_id: str
|
|
431
|
+
relation: RelationType
|
|
432
|
+
|
|
433
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
434
|
+
return {
|
|
435
|
+
"source_id": self.source_id,
|
|
436
|
+
"target_id": self.target_id,
|
|
437
|
+
"relation": self.relation.value
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
@classmethod
|
|
441
|
+
def from_dict(cls, data: Dict[str, Any]) -> "GraphEdge":
|
|
442
|
+
return cls(
|
|
443
|
+
source_id=data["source_id"],
|
|
444
|
+
target_id=data["target_id"],
|
|
445
|
+
relation=RelationType(data["relation"])
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# ==============================================================================
|
|
450
|
+
# AGGREGATE CANONICAL PROJECT STATE (ROOT CONTAINER)
|
|
451
|
+
# ==============================================================================
|
|
452
|
+
|
|
453
|
+
@dataclass
|
|
454
|
+
class CanonicalProjectState:
|
|
455
|
+
"""
|
|
456
|
+
Root container for the complete verified state of Project Continuum.
|
|
457
|
+
Preserves versioning, strict state isolation, the canonical graph,
|
|
458
|
+
contradictions, and all referenced evidence.
|
|
459
|
+
"""
|
|
460
|
+
schema_version: str = "1.0.0"
|
|
461
|
+
project_id: str = field(default_factory=lambda: f"proj_{uuid.uuid4().hex[:8]}")
|
|
462
|
+
created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
463
|
+
updated_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
464
|
+
|
|
465
|
+
# The 3 segregated states:
|
|
466
|
+
project_state: ProjectState = field(default_factory=lambda: ProjectState(root_path=""))
|
|
467
|
+
conversational_state: ConversationalState = field(default_factory=ConversationalState)
|
|
468
|
+
agent_execution_state: AgentExecutionState = field(default_factory=AgentExecutionState)
|
|
469
|
+
|
|
470
|
+
# Global evidence pool indexed by evidence.id:
|
|
471
|
+
evidence_pool: Dict[str, Evidence] = field(default_factory=dict)
|
|
472
|
+
|
|
473
|
+
# Canonical State Graph (DAG):
|
|
474
|
+
graph_nodes: Dict[str, GraphNode] = field(default_factory=dict)
|
|
475
|
+
graph_edges: List[GraphEdge] = field(default_factory=list)
|
|
476
|
+
|
|
477
|
+
# Discrepancies and Contradictions Ledger:
|
|
478
|
+
contradictions: List[ContradictionRecord] = field(default_factory=list)
|
|
479
|
+
|
|
480
|
+
def add_evidence(self, evidence: Evidence) -> str:
|
|
481
|
+
"""Registers a piece of evidence in the global pool and returns its ID."""
|
|
482
|
+
self.evidence_pool[evidence.id] = evidence
|
|
483
|
+
return evidence.id
|
|
484
|
+
|
|
485
|
+
def get_evidence(self, evidence_id: str) -> Optional[Evidence]:
|
|
486
|
+
return self.evidence_pool.get(evidence_id)
|
|
487
|
+
|
|
488
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
489
|
+
return {
|
|
490
|
+
"schema_version": self.schema_version,
|
|
491
|
+
"project_id": self.project_id,
|
|
492
|
+
"created_at": self.created_at,
|
|
493
|
+
"updated_at": self.updated_at,
|
|
494
|
+
"project_state": self.project_state.to_dict(),
|
|
495
|
+
"conversational_state": self.conversational_state.to_dict(),
|
|
496
|
+
"agent_execution_state": self.agent_execution_state.to_dict(),
|
|
497
|
+
"evidence_pool": {k: v.to_dict() for k, v in self.evidence_pool.items()},
|
|
498
|
+
"graph_nodes": {k: v.to_dict() for k, v in self.graph_nodes.items()},
|
|
499
|
+
"graph_edges": [e.to_dict() for e in self.graph_edges],
|
|
500
|
+
"contradictions": [c.to_dict() for c in self.contradictions]
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
@classmethod
|
|
504
|
+
def from_dict(cls, data: Dict[str, Any]) -> "CanonicalProjectState":
|
|
505
|
+
ev_pool = {
|
|
506
|
+
k: Evidence.from_dict(v) for k, v in data.get("evidence_pool", {}).items()
|
|
507
|
+
}
|
|
508
|
+
g_nodes = {
|
|
509
|
+
k: GraphNode.from_dict(v) for k, v in data.get("graph_nodes", {}).items()
|
|
510
|
+
}
|
|
511
|
+
g_edges = [
|
|
512
|
+
GraphEdge.from_dict(e) for e in data.get("graph_edges", [])
|
|
513
|
+
]
|
|
514
|
+
contradictions = [
|
|
515
|
+
ContradictionRecord.from_dict(c) for c in data.get("contradictions", [])
|
|
516
|
+
]
|
|
517
|
+
|
|
518
|
+
return cls(
|
|
519
|
+
schema_version=data.get("schema_version", "1.0.0"),
|
|
520
|
+
project_id=data.get("project_id", f"proj_{uuid.uuid4().hex[:8]}"),
|
|
521
|
+
created_at=data.get("created_at", datetime.now(timezone.utc).isoformat()),
|
|
522
|
+
updated_at=data.get("updated_at", datetime.now(timezone.utc).isoformat()),
|
|
523
|
+
project_state=ProjectState.from_dict(data.get("project_state", {})),
|
|
524
|
+
conversational_state=ConversationalState.from_dict(data.get("conversational_state", {})),
|
|
525
|
+
agent_execution_state=AgentExecutionState.from_dict(data.get("agent_execution_state", {})),
|
|
526
|
+
evidence_pool=ev_pool,
|
|
527
|
+
graph_nodes=g_nodes,
|
|
528
|
+
graph_edges=g_edges,
|
|
529
|
+
contradictions=contradictions
|
|
530
|
+
)
|
daemon/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project Continuum - Daemon Package Exports
|
|
3
|
+
==========================================
|
|
4
|
+
Milestone 7 - Phase 18: Continuum Daemon & CLI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from daemon.service import ContinuumDaemon, DaemonStatus
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ContinuumDaemon",
|
|
11
|
+
"DaemonStatus",
|
|
12
|
+
]
|