shellbrain 0.1.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.
- app/__init__.py +1 -0
- app/__main__.py +7 -0
- app/boot/__init__.py +1 -0
- app/boot/admin_db.py +88 -0
- app/boot/config.py +14 -0
- app/boot/create_policy.py +52 -0
- app/boot/db.py +70 -0
- app/boot/embeddings.py +55 -0
- app/boot/home.py +45 -0
- app/boot/migrations.py +61 -0
- app/boot/read_policy.py +179 -0
- app/boot/repos.py +15 -0
- app/boot/retrieval.py +3 -0
- app/boot/thresholds.py +19 -0
- app/boot/update_policy.py +34 -0
- app/boot/use_cases.py +22 -0
- app/config/__init__.py +1 -0
- app/config/defaults/create_policy.yaml +7 -0
- app/config/defaults/read_policy.yaml +25 -0
- app/config/defaults/runtime.yaml +10 -0
- app/config/defaults/thresholds.yaml +3 -0
- app/config/defaults/update_policy.yaml +5 -0
- app/config/loader.py +58 -0
- app/core/__init__.py +1 -0
- app/core/contracts/__init__.py +1 -0
- app/core/contracts/errors.py +29 -0
- app/core/contracts/requests.py +211 -0
- app/core/contracts/responses.py +15 -0
- app/core/entities/__init__.py +1 -0
- app/core/entities/associations.py +58 -0
- app/core/entities/episodes.py +66 -0
- app/core/entities/evidence.py +29 -0
- app/core/entities/facts.py +30 -0
- app/core/entities/guidance.py +47 -0
- app/core/entities/identity.py +48 -0
- app/core/entities/memory.py +34 -0
- app/core/entities/runtime_context.py +19 -0
- app/core/entities/session_state.py +31 -0
- app/core/entities/telemetry.py +152 -0
- app/core/entities/utility.py +14 -0
- app/core/interfaces/__init__.py +1 -0
- app/core/interfaces/clock.py +12 -0
- app/core/interfaces/config.py +28 -0
- app/core/interfaces/embeddings.py +12 -0
- app/core/interfaces/idgen.py +11 -0
- app/core/interfaces/repos.py +279 -0
- app/core/interfaces/retrieval.py +20 -0
- app/core/interfaces/session_state_store.py +33 -0
- app/core/interfaces/unit_of_work.py +50 -0
- app/core/policies/__init__.py +1 -0
- app/core/policies/_shared/__init__.py +1 -0
- app/core/policies/_shared/executor.py +132 -0
- app/core/policies/_shared/side_effects.py +9 -0
- app/core/policies/create_policy/__init__.py +1 -0
- app/core/policies/create_policy/pipeline.py +96 -0
- app/core/policies/read_policy/__init__.py +1 -0
- app/core/policies/read_policy/bm25.py +114 -0
- app/core/policies/read_policy/context_pack_builder.py +140 -0
- app/core/policies/read_policy/expansion.py +132 -0
- app/core/policies/read_policy/fusion_rrf.py +34 -0
- app/core/policies/read_policy/lexical_query.py +101 -0
- app/core/policies/read_policy/pipeline.py +93 -0
- app/core/policies/read_policy/scenario_lift.py +11 -0
- app/core/policies/read_policy/scoring.py +61 -0
- app/core/policies/read_policy/seed_retrieval.py +54 -0
- app/core/policies/read_policy/utility_prior.py +11 -0
- app/core/policies/update_policy/__init__.py +1 -0
- app/core/policies/update_policy/pipeline.py +80 -0
- app/core/use_cases/__init__.py +1 -0
- app/core/use_cases/build_guidance.py +85 -0
- app/core/use_cases/create_memory.py +26 -0
- app/core/use_cases/manage_session_state.py +159 -0
- app/core/use_cases/read_memory.py +21 -0
- app/core/use_cases/record_episode_sync_telemetry.py +19 -0
- app/core/use_cases/record_operation_telemetry.py +32 -0
- app/core/use_cases/sync_episode.py +162 -0
- app/core/use_cases/update_memory.py +40 -0
- app/migrations/__init__.py +1 -0
- app/migrations/env.py +65 -0
- app/migrations/versions/20260226_0001_initial_schema.py +232 -0
- app/migrations/versions/20260312_0002_add_hard_invariants.py +60 -0
- app/migrations/versions/20260312_0003_drop_create_confidence.py +40 -0
- app/migrations/versions/20260313_0004_episode_sync_hardening.py +71 -0
- app/migrations/versions/20260313_0005_evidence_episode_event_refs.py +45 -0
- app/migrations/versions/20260318_0006_usage_telemetry_schema.py +175 -0
- app/migrations/versions/20260319_0007_identity_session_guidance.py +49 -0
- app/migrations/versions/20260320_0008_instance_metadata_and_backup_safety.py +31 -0
- app/migrations/versions/__init__.py +1 -0
- app/periphery/__init__.py +1 -0
- app/periphery/admin/__init__.py +1 -0
- app/periphery/admin/backup.py +360 -0
- app/periphery/admin/destructive_guard.py +32 -0
- app/periphery/admin/doctor.py +192 -0
- app/periphery/admin/init.py +996 -0
- app/periphery/admin/instance_guard.py +211 -0
- app/periphery/admin/machine_state.py +354 -0
- app/periphery/admin/privileges.py +42 -0
- app/periphery/admin/repo_state.py +266 -0
- app/periphery/admin/restore.py +30 -0
- app/periphery/cli/__init__.py +1 -0
- app/periphery/cli/handlers.py +830 -0
- app/periphery/cli/hydration.py +119 -0
- app/periphery/cli/main.py +710 -0
- app/periphery/cli/presenter_json.py +10 -0
- app/periphery/cli/schema_validation.py +201 -0
- app/periphery/db/__init__.py +1 -0
- app/periphery/db/engine.py +10 -0
- app/periphery/db/models/__init__.py +1 -0
- app/periphery/db/models/associations.py +55 -0
- app/periphery/db/models/episodes.py +55 -0
- app/periphery/db/models/evidence.py +19 -0
- app/periphery/db/models/experiences.py +33 -0
- app/periphery/db/models/instance_metadata.py +17 -0
- app/periphery/db/models/memories.py +39 -0
- app/periphery/db/models/metadata.py +6 -0
- app/periphery/db/models/registry.py +18 -0
- app/periphery/db/models/telemetry.py +174 -0
- app/periphery/db/models/utility.py +19 -0
- app/periphery/db/models/views.py +154 -0
- app/periphery/db/repos/__init__.py +1 -0
- app/periphery/db/repos/relational/__init__.py +1 -0
- app/periphery/db/repos/relational/associations_repo.py +117 -0
- app/periphery/db/repos/relational/episodes_repo.py +188 -0
- app/periphery/db/repos/relational/evidence_repo.py +82 -0
- app/periphery/db/repos/relational/experiences_repo.py +41 -0
- app/periphery/db/repos/relational/memories_repo.py +99 -0
- app/periphery/db/repos/relational/read_policy_repo.py +202 -0
- app/periphery/db/repos/relational/telemetry_repo.py +161 -0
- app/periphery/db/repos/relational/utility_repo.py +30 -0
- app/periphery/db/repos/semantic/__init__.py +1 -0
- app/periphery/db/repos/semantic/keyword_retrieval_repo.py +63 -0
- app/periphery/db/repos/semantic/semantic_retrieval_repo.py +111 -0
- app/periphery/db/session.py +10 -0
- app/periphery/db/uow.py +75 -0
- app/periphery/embeddings/__init__.py +1 -0
- app/periphery/embeddings/local_provider.py +35 -0
- app/periphery/embeddings/query_vector_search.py +18 -0
- app/periphery/episodes/__init__.py +1 -0
- app/periphery/episodes/claude_code.py +387 -0
- app/periphery/episodes/codex.py +423 -0
- app/periphery/episodes/launcher.py +66 -0
- app/periphery/episodes/normalization.py +31 -0
- app/periphery/episodes/poller.py +299 -0
- app/periphery/episodes/source_discovery.py +66 -0
- app/periphery/episodes/tool_filter.py +165 -0
- app/periphery/identity/__init__.py +1 -0
- app/periphery/identity/claude_hook_install.py +67 -0
- app/periphery/identity/claude_runtime.py +83 -0
- app/periphery/identity/codex_runtime.py +32 -0
- app/periphery/identity/compatibility.py +38 -0
- app/periphery/identity/resolver.py +163 -0
- app/periphery/session_state/__init__.py +1 -0
- app/periphery/session_state/file_store.py +100 -0
- app/periphery/telemetry/__init__.py +33 -0
- app/periphery/telemetry/operation_summary.py +299 -0
- app/periphery/telemetry/session_selection.py +156 -0
- app/periphery/telemetry/sync_summary.py +65 -0
- app/periphery/validation/__init__.py +1 -0
- app/periphery/validation/integrity_validation.py +253 -0
- app/periphery/validation/semantic_validation.py +94 -0
- shellbrain-0.1.0.dist-info/METADATA +130 -0
- shellbrain-0.1.0.dist-info/RECORD +165 -0
- shellbrain-0.1.0.dist-info/WHEEL +5 -0
- shellbrain-0.1.0.dist-info/entry_points.txt +2 -0
- shellbrain-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Core caller-identity concepts used across runtime, episodes, and telemetry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IdentityTrustLevel(str, Enum):
|
|
10
|
+
"""Supported caller-identity trust levels."""
|
|
11
|
+
|
|
12
|
+
TRUSTED = "trusted"
|
|
13
|
+
UNTRUSTED = "untrusted"
|
|
14
|
+
UNSUPPORTED = "unsupported"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_canonical_caller_id(*, host_app: str, host_session_key: str, agent_key: str | None = None) -> str:
|
|
18
|
+
"""Build the canonical caller identifier from one host session and optional agent key."""
|
|
19
|
+
|
|
20
|
+
canonical = f"{host_app}:{host_session_key}"
|
|
21
|
+
if agent_key:
|
|
22
|
+
canonical = f"{canonical}:agent:{agent_key}"
|
|
23
|
+
return canonical
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, kw_only=True)
|
|
27
|
+
class CallerIdentity:
|
|
28
|
+
"""Canonical caller identity used to scope episodes, session state, and guidance."""
|
|
29
|
+
|
|
30
|
+
host_app: str
|
|
31
|
+
host_session_key: str
|
|
32
|
+
agent_key: str | None = None
|
|
33
|
+
canonical_id: str | None = None
|
|
34
|
+
trust_level: IdentityTrustLevel = IdentityTrustLevel.UNTRUSTED
|
|
35
|
+
|
|
36
|
+
def __post_init__(self) -> None:
|
|
37
|
+
"""Fill the canonical id when the caller omits it."""
|
|
38
|
+
|
|
39
|
+
if self.canonical_id is None:
|
|
40
|
+
object.__setattr__(
|
|
41
|
+
self,
|
|
42
|
+
"canonical_id",
|
|
43
|
+
build_canonical_caller_id(
|
|
44
|
+
host_app=self.host_app,
|
|
45
|
+
host_session_key=self.host_session_key,
|
|
46
|
+
agent_key=self.agent_key,
|
|
47
|
+
),
|
|
48
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""This module defines immutable shellbrain entities and core shellbrain enums."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MemoryKind(str, Enum):
|
|
8
|
+
"""This enum defines ratified atomic shellbrain kinds."""
|
|
9
|
+
|
|
10
|
+
PROBLEM = "problem"
|
|
11
|
+
SOLUTION = "solution"
|
|
12
|
+
FAILED_TACTIC = "failed_tactic"
|
|
13
|
+
FACT = "fact"
|
|
14
|
+
PREFERENCE = "preference"
|
|
15
|
+
CHANGE = "change"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MemoryScope(str, Enum):
|
|
19
|
+
"""This enum defines shellbrain visibility scope."""
|
|
20
|
+
|
|
21
|
+
REPO = "repo"
|
|
22
|
+
GLOBAL = "global"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(kw_only=True)
|
|
26
|
+
class Memory:
|
|
27
|
+
"""This dataclass models an immutable shellbrain record."""
|
|
28
|
+
|
|
29
|
+
id: str
|
|
30
|
+
repo_id: str
|
|
31
|
+
scope: MemoryScope
|
|
32
|
+
kind: MemoryKind
|
|
33
|
+
text: str
|
|
34
|
+
archived: bool = False
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Per-invocation runtime context shared across CLI handlers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from app.core.contracts.errors import ErrorDetail
|
|
8
|
+
from app.core.entities.identity import CallerIdentity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class RuntimeContext:
|
|
13
|
+
"""Per-command context captured in CLI main and consumed by handlers."""
|
|
14
|
+
|
|
15
|
+
invocation_id: str
|
|
16
|
+
repo_root: str
|
|
17
|
+
no_sync: bool = False
|
|
18
|
+
caller_identity: CallerIdentity | None = None
|
|
19
|
+
caller_identity_error: ErrorDetail | None = None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Core session-state entities for per-caller working memory."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SessionStateResetReason(str, Enum):
|
|
10
|
+
"""Reasons a working session may be reset while preserving caller identity metadata."""
|
|
11
|
+
|
|
12
|
+
IDLE_EXPIRED = "idle_expired"
|
|
13
|
+
CALLER_SWITCHED = "caller_switched"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(kw_only=True)
|
|
17
|
+
class SessionState:
|
|
18
|
+
"""Per-caller repo-local working state used to drive exact events and guidance."""
|
|
19
|
+
|
|
20
|
+
caller_id: str
|
|
21
|
+
host_app: str
|
|
22
|
+
host_session_key: str
|
|
23
|
+
agent_key: str | None = None
|
|
24
|
+
session_started_at: str
|
|
25
|
+
last_seen_at: str
|
|
26
|
+
current_problem_id: str | None = None
|
|
27
|
+
last_events_episode_id: str | None = None
|
|
28
|
+
last_events_event_ids: list[str] = field(default_factory=list)
|
|
29
|
+
last_events_at: str | None = None
|
|
30
|
+
last_guidance_at: str | None = None
|
|
31
|
+
last_guidance_problem_id: str | None = None
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Internal telemetry entities used to persist low-overhead usage analytics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from app.core.entities.runtime_context import RuntimeContext as OperationDispatchTelemetryContext
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class SessionSelectionSummary:
|
|
14
|
+
"""Resolved session/thread context recorded alongside one command invocation."""
|
|
15
|
+
|
|
16
|
+
selected_host_app: str | None = None
|
|
17
|
+
selected_host_session_key: str | None = None
|
|
18
|
+
selected_thread_id: str | None = None
|
|
19
|
+
selected_episode_id: str | None = None
|
|
20
|
+
matching_candidate_count: int = 0
|
|
21
|
+
selection_ambiguous: bool = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class OperationInvocationRecord:
|
|
26
|
+
"""Append-only parent row for one operational command invocation."""
|
|
27
|
+
|
|
28
|
+
id: str
|
|
29
|
+
command: str
|
|
30
|
+
repo_id: str
|
|
31
|
+
repo_root: str
|
|
32
|
+
no_sync: bool
|
|
33
|
+
caller_id: str | None
|
|
34
|
+
caller_trust_level: str | None
|
|
35
|
+
identity_failure_code: str | None
|
|
36
|
+
selected_host_app: str | None
|
|
37
|
+
selected_host_session_key: str | None
|
|
38
|
+
selected_thread_id: str | None
|
|
39
|
+
selected_episode_id: str | None
|
|
40
|
+
matching_candidate_count: int
|
|
41
|
+
selection_ambiguous: bool
|
|
42
|
+
outcome: str
|
|
43
|
+
error_stage: str | None
|
|
44
|
+
error_code: str | None
|
|
45
|
+
error_message: str | None
|
|
46
|
+
total_latency_ms: int
|
|
47
|
+
poller_start_attempted: bool
|
|
48
|
+
poller_started: bool
|
|
49
|
+
created_at: datetime
|
|
50
|
+
guidance_codes: list[str] = field(default_factory=list)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class ReadSummaryRecord:
|
|
55
|
+
"""One summary row describing a successful read invocation."""
|
|
56
|
+
|
|
57
|
+
invocation_id: str
|
|
58
|
+
query_text: str
|
|
59
|
+
mode: str
|
|
60
|
+
requested_limit: int | None
|
|
61
|
+
effective_limit: int
|
|
62
|
+
include_global: bool | None
|
|
63
|
+
kinds_filter: list[str] | None
|
|
64
|
+
direct_count: int
|
|
65
|
+
explicit_related_count: int
|
|
66
|
+
implicit_related_count: int
|
|
67
|
+
total_returned: int
|
|
68
|
+
zero_results: bool
|
|
69
|
+
created_at: datetime
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class ReadResultItemRecord:
|
|
74
|
+
"""One displayed read result item in stable pack order."""
|
|
75
|
+
|
|
76
|
+
invocation_id: str
|
|
77
|
+
ordinal: int
|
|
78
|
+
memory_id: str
|
|
79
|
+
kind: str
|
|
80
|
+
section: str
|
|
81
|
+
priority: int
|
|
82
|
+
why_included: str
|
|
83
|
+
anchor_memory_id: str | None
|
|
84
|
+
relation_type: str | None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class WriteSummaryRecord:
|
|
89
|
+
"""One summary row describing a successful create or update invocation."""
|
|
90
|
+
|
|
91
|
+
invocation_id: str
|
|
92
|
+
operation_command: str
|
|
93
|
+
target_memory_id: str
|
|
94
|
+
target_kind: str | None
|
|
95
|
+
update_type: str | None
|
|
96
|
+
scope: str | None
|
|
97
|
+
evidence_ref_count: int
|
|
98
|
+
planned_effect_count: int
|
|
99
|
+
created_memory_count: int
|
|
100
|
+
archived_memory_count: int
|
|
101
|
+
utility_observation_count: int
|
|
102
|
+
association_effect_count: int
|
|
103
|
+
fact_update_count: int
|
|
104
|
+
created_at: datetime
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class WriteEffectItemRecord:
|
|
109
|
+
"""One compact side-effect row attached to a write invocation."""
|
|
110
|
+
|
|
111
|
+
invocation_id: str
|
|
112
|
+
ordinal: int
|
|
113
|
+
effect_type: str
|
|
114
|
+
repo_id: str
|
|
115
|
+
primary_memory_id: str | None
|
|
116
|
+
secondary_memory_id: str | None
|
|
117
|
+
params_json: dict[str, Any] = field(default_factory=dict)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class EpisodeSyncRunRecord:
|
|
122
|
+
"""One inline or poller sync attempt."""
|
|
123
|
+
|
|
124
|
+
id: str
|
|
125
|
+
source: str
|
|
126
|
+
invocation_id: str | None
|
|
127
|
+
repo_id: str
|
|
128
|
+
host_app: str
|
|
129
|
+
host_session_key: str
|
|
130
|
+
thread_id: str
|
|
131
|
+
episode_id: str | None
|
|
132
|
+
transcript_path: str | None
|
|
133
|
+
outcome: str
|
|
134
|
+
error_stage: str | None
|
|
135
|
+
error_message: str | None
|
|
136
|
+
duration_ms: int
|
|
137
|
+
imported_event_count: int
|
|
138
|
+
total_event_count: int
|
|
139
|
+
user_event_count: int
|
|
140
|
+
assistant_event_count: int
|
|
141
|
+
tool_event_count: int
|
|
142
|
+
system_event_count: int
|
|
143
|
+
created_at: datetime
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass(frozen=True)
|
|
147
|
+
class EpisodeSyncToolTypeRecord:
|
|
148
|
+
"""Aggregated per-tool counts for one sync run."""
|
|
149
|
+
|
|
150
|
+
sync_run_id: str
|
|
151
|
+
tool_type: str
|
|
152
|
+
event_count: int
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""This module defines utility-observation entities used for contextual feedback."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(kw_only=True)
|
|
7
|
+
class UtilityObservation:
|
|
8
|
+
"""This dataclass models a utility vote linked to shellbrain and problem context."""
|
|
9
|
+
|
|
10
|
+
id: str
|
|
11
|
+
memory_id: str
|
|
12
|
+
problem_id: str
|
|
13
|
+
vote: float
|
|
14
|
+
rationale: str | None = None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""This package defines core interface abstractions for periphery adapters."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""This module defines an interface for obtaining current time values."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IClock(ABC):
|
|
8
|
+
"""This interface defines a deterministic clock boundary."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def now(self) -> datetime:
|
|
12
|
+
"""This method returns the current timestamp."""
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""This module defines configuration provider interfaces for policy and runtime values."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IConfigProvider(ABC):
|
|
8
|
+
"""This interface defines accessors for policy and runtime configuration sections."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def get_read_policy(self) -> dict[str, Any]:
|
|
12
|
+
"""This method returns read-policy configuration values."""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def get_create_policy(self) -> dict[str, Any]:
|
|
16
|
+
"""This method returns create-policy configuration values."""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def get_update_policy(self) -> dict[str, Any]:
|
|
20
|
+
"""This method returns update-policy configuration values."""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_thresholds(self) -> dict[str, Any]:
|
|
24
|
+
"""This method returns threshold configuration values."""
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def get_runtime(self) -> dict[str, Any]:
|
|
28
|
+
"""This method returns runtime configuration values."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""This module defines a boundary for generating embedding vectors from text."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Sequence
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IEmbeddingProvider(ABC):
|
|
8
|
+
"""This interface defines embedding generation for create-time shellbrain writes."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def embed(self, text: str) -> Sequence[float]:
|
|
12
|
+
"""This method returns an embedding vector for the given text."""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""This module defines an interface for generating stable identifiers."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IIdGenerator(ABC):
|
|
7
|
+
"""This interface defines generation of unique string identifiers."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def new_id(self) -> str:
|
|
11
|
+
"""This method returns a new unique identifier string."""
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""This module defines repository interfaces for relational and semantic data access."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any, Sequence
|
|
6
|
+
|
|
7
|
+
from app.core.entities.associations import AssociationEdge, AssociationObservation
|
|
8
|
+
from app.core.entities.evidence import EvidenceRef
|
|
9
|
+
from app.core.entities.episodes import Episode, EpisodeEvent, SessionTransfer
|
|
10
|
+
from app.core.entities.facts import FactUpdate, ProblemAttempt
|
|
11
|
+
from app.core.entities.memory import Memory
|
|
12
|
+
from app.core.entities.telemetry import (
|
|
13
|
+
EpisodeSyncRunRecord,
|
|
14
|
+
EpisodeSyncToolTypeRecord,
|
|
15
|
+
OperationInvocationRecord,
|
|
16
|
+
ReadResultItemRecord,
|
|
17
|
+
ReadSummaryRecord,
|
|
18
|
+
WriteEffectItemRecord,
|
|
19
|
+
WriteSummaryRecord,
|
|
20
|
+
)
|
|
21
|
+
from app.core.entities.guidance import PendingUtilityCandidate
|
|
22
|
+
from app.core.entities.utility import UtilityObservation
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IMemoriesRepo(ABC):
|
|
26
|
+
"""This interface defines persistence operations for shellbrain aggregates."""
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def create(self, memory: Memory) -> None:
|
|
30
|
+
"""This method persists a shellbrain record."""
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def get(self, memory_id: str) -> Memory | None:
|
|
34
|
+
"""This method fetches a shellbrain by identifier."""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def list_by_ids(self, ids: Sequence[str]) -> Sequence[Memory]:
|
|
38
|
+
"""This method fetches memories in the input identifier order."""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def set_archived(self, *, memory_id: str, archived: bool) -> bool:
|
|
42
|
+
"""This method updates the archived state for a shellbrain and reports whether a row changed."""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def upsert_embedding(self, *, memory_id: str, model: str, vector: Sequence[float]) -> None:
|
|
46
|
+
"""This method inserts or updates the embedding vector record for a memory."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IExperiencesRepo(ABC):
|
|
50
|
+
"""This interface defines persistence operations for problem attempts and fact updates."""
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def create_problem_attempt(self, attempt: ProblemAttempt) -> None:
|
|
54
|
+
"""This method persists a problem-attempt link."""
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def create_fact_update(self, fact_update: FactUpdate) -> None:
|
|
58
|
+
"""This method persists a fact-update chain row."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class IAssociationsRepo(ABC):
|
|
62
|
+
"""This interface defines persistence operations for association edges and observations."""
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def upsert_edge(self, edge: AssociationEdge) -> AssociationEdge:
|
|
66
|
+
"""This method inserts or updates an association edge."""
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def append_observation(self, observation: AssociationObservation) -> None:
|
|
70
|
+
"""This method appends an immutable association observation."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IUtilityRepo(ABC):
|
|
74
|
+
"""This interface defines persistence operations for utility observations."""
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def append_observation(self, observation: UtilityObservation) -> None:
|
|
78
|
+
"""This method appends a utility observation entry."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class IEpisodesRepo(ABC):
|
|
82
|
+
"""This interface defines persistence operations for episodes and events."""
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def create_episode(self, episode: Episode) -> None:
|
|
86
|
+
"""This method persists an episode row."""
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def get_episode_by_thread(
|
|
90
|
+
self,
|
|
91
|
+
*,
|
|
92
|
+
repo_id: str,
|
|
93
|
+
thread_id: str,
|
|
94
|
+
) -> Episode | None:
|
|
95
|
+
"""This method fetches one episode by canonical host session key."""
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def list_event_keys(self, *, episode_id: str) -> Sequence[str]:
|
|
99
|
+
"""This method returns already-imported upstream event keys for one episode."""
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def next_event_seq(self, *, episode_id: str) -> int:
|
|
103
|
+
"""This method returns the next append sequence number for one episode."""
|
|
104
|
+
|
|
105
|
+
@abstractmethod
|
|
106
|
+
def append_event(self, event: EpisodeEvent) -> None:
|
|
107
|
+
"""This method appends an event into an episode stream."""
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def close_episode(self, *, episode_id: str, ended_at: datetime) -> None:
|
|
111
|
+
"""This method marks an active episode closed."""
|
|
112
|
+
|
|
113
|
+
@abstractmethod
|
|
114
|
+
def append_transfer(self, transfer: SessionTransfer) -> None:
|
|
115
|
+
"""This method appends a cross-session transfer row."""
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def list_existing_event_ids(self, *, event_ids: Sequence[str]) -> Sequence[str]:
|
|
119
|
+
"""This method returns episode-event ids that exist anywhere in storage."""
|
|
120
|
+
|
|
121
|
+
@abstractmethod
|
|
122
|
+
def list_visible_event_ids(self, *, repo_id: str, event_ids: Sequence[str]) -> Sequence[str]:
|
|
123
|
+
"""This method returns episode-event ids visible within one repo."""
|
|
124
|
+
|
|
125
|
+
@abstractmethod
|
|
126
|
+
def list_recent_events(
|
|
127
|
+
self,
|
|
128
|
+
*,
|
|
129
|
+
repo_id: str,
|
|
130
|
+
episode_id: str,
|
|
131
|
+
limit: int,
|
|
132
|
+
) -> Sequence[EpisodeEvent]:
|
|
133
|
+
"""This method returns recent events for one visible episode ordered newest first."""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class IEvidenceRepo(ABC):
|
|
137
|
+
"""This interface defines persistence operations for evidence references and links."""
|
|
138
|
+
|
|
139
|
+
@abstractmethod
|
|
140
|
+
def upsert_ref(self, repo_id: str, ref: str) -> EvidenceRef:
|
|
141
|
+
"""This method inserts or returns an evidence reference."""
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def link_memory_evidence(self, memory_id: str, evidence_id: str) -> None:
|
|
145
|
+
"""This method links a shellbrain to an evidence reference."""
|
|
146
|
+
|
|
147
|
+
@abstractmethod
|
|
148
|
+
def link_association_edge_evidence(self, edge_id: str, evidence_id: str) -> None:
|
|
149
|
+
"""This method links an association edge to an evidence reference."""
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ISemanticRetrievalRepo(ABC):
|
|
153
|
+
"""This interface defines semantic-lane retrieval against embeddings."""
|
|
154
|
+
|
|
155
|
+
@abstractmethod
|
|
156
|
+
def query_semantic(
|
|
157
|
+
self,
|
|
158
|
+
*,
|
|
159
|
+
repo_id: str,
|
|
160
|
+
include_global: bool,
|
|
161
|
+
query_vector: Sequence[float],
|
|
162
|
+
kinds: Sequence[str] | None,
|
|
163
|
+
limit: int,
|
|
164
|
+
) -> Sequence[dict[str, Any]]:
|
|
165
|
+
"""This method returns semantic retrieval candidates with scores."""
|
|
166
|
+
|
|
167
|
+
@abstractmethod
|
|
168
|
+
def list_semantic_neighbors(
|
|
169
|
+
self,
|
|
170
|
+
*,
|
|
171
|
+
repo_id: str,
|
|
172
|
+
include_global: bool,
|
|
173
|
+
anchor_memory_id: str,
|
|
174
|
+
kinds: Sequence[str] | None,
|
|
175
|
+
limit: int | None = None,
|
|
176
|
+
) -> Sequence[dict[str, Any]]:
|
|
177
|
+
"""This method returns implicit semantic neighbors for one anchor memory."""
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class IKeywordRetrievalRepo(ABC):
|
|
181
|
+
"""This interface defines keyword-lane retrieval against the lexical relevance engine."""
|
|
182
|
+
|
|
183
|
+
@abstractmethod
|
|
184
|
+
def query_keyword(
|
|
185
|
+
self,
|
|
186
|
+
*,
|
|
187
|
+
repo_id: str,
|
|
188
|
+
mode: str,
|
|
189
|
+
include_global: bool,
|
|
190
|
+
query_text: str,
|
|
191
|
+
kinds: Sequence[str] | None,
|
|
192
|
+
limit: int,
|
|
193
|
+
) -> Sequence[dict[str, Any]]:
|
|
194
|
+
"""This method returns lexical retrieval candidates with scores."""
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class IReadPolicyRepo(ABC):
|
|
198
|
+
"""This interface defines read-path visibility and explicit expansion queries."""
|
|
199
|
+
|
|
200
|
+
@abstractmethod
|
|
201
|
+
def list_problem_attempt_neighbors(
|
|
202
|
+
self,
|
|
203
|
+
*,
|
|
204
|
+
repo_id: str,
|
|
205
|
+
include_global: bool,
|
|
206
|
+
anchor_memory_id: str,
|
|
207
|
+
kinds: Sequence[str] | None,
|
|
208
|
+
) -> Sequence[dict[str, Any]]:
|
|
209
|
+
"""This method returns visible problem-attempt neighbors for an anchor memory."""
|
|
210
|
+
|
|
211
|
+
@abstractmethod
|
|
212
|
+
def list_fact_update_neighbors(
|
|
213
|
+
self,
|
|
214
|
+
*,
|
|
215
|
+
repo_id: str,
|
|
216
|
+
include_global: bool,
|
|
217
|
+
anchor_memory_id: str,
|
|
218
|
+
kinds: Sequence[str] | None,
|
|
219
|
+
) -> Sequence[dict[str, Any]]:
|
|
220
|
+
"""This method returns visible fact-update neighbors for an anchor memory."""
|
|
221
|
+
|
|
222
|
+
@abstractmethod
|
|
223
|
+
def list_association_neighbors(
|
|
224
|
+
self,
|
|
225
|
+
*,
|
|
226
|
+
repo_id: str,
|
|
227
|
+
include_global: bool,
|
|
228
|
+
anchor_memory_id: str,
|
|
229
|
+
kinds: Sequence[str] | None,
|
|
230
|
+
min_strength: float,
|
|
231
|
+
) -> Sequence[dict[str, Any]]:
|
|
232
|
+
"""This method returns visible association neighbors for an anchor memory."""
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class ITelemetryRepo(ABC):
|
|
236
|
+
"""This interface defines append-heavy telemetry persistence operations."""
|
|
237
|
+
|
|
238
|
+
@abstractmethod
|
|
239
|
+
def insert_operation_invocation(self, record: OperationInvocationRecord) -> None:
|
|
240
|
+
"""This method appends one command-level telemetry row."""
|
|
241
|
+
|
|
242
|
+
@abstractmethod
|
|
243
|
+
def insert_read_summary(
|
|
244
|
+
self,
|
|
245
|
+
summary: ReadSummaryRecord,
|
|
246
|
+
items: Sequence[ReadResultItemRecord],
|
|
247
|
+
) -> None:
|
|
248
|
+
"""This method persists one read summary row and its ordered result items."""
|
|
249
|
+
|
|
250
|
+
@abstractmethod
|
|
251
|
+
def insert_write_summary(
|
|
252
|
+
self,
|
|
253
|
+
summary: WriteSummaryRecord,
|
|
254
|
+
items: Sequence[WriteEffectItemRecord],
|
|
255
|
+
) -> None:
|
|
256
|
+
"""This method persists one write summary row and its ordered effect items."""
|
|
257
|
+
|
|
258
|
+
@abstractmethod
|
|
259
|
+
def insert_episode_sync_run(
|
|
260
|
+
self,
|
|
261
|
+
run: EpisodeSyncRunRecord,
|
|
262
|
+
tool_types: Sequence[EpisodeSyncToolTypeRecord],
|
|
263
|
+
) -> None:
|
|
264
|
+
"""This method appends one sync-run row and its per-tool aggregates."""
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
def update_operation_polling(self, invocation_id: str, *, attempted: bool, started: bool) -> None:
|
|
268
|
+
"""This method patches the poller-start flags for one existing invocation row."""
|
|
269
|
+
|
|
270
|
+
@abstractmethod
|
|
271
|
+
def list_pending_utility_candidates(
|
|
272
|
+
self,
|
|
273
|
+
*,
|
|
274
|
+
repo_id: str,
|
|
275
|
+
caller_id: str,
|
|
276
|
+
problem_id: str,
|
|
277
|
+
since_iso: str,
|
|
278
|
+
) -> Sequence[PendingUtilityCandidate]:
|
|
279
|
+
"""This method returns retrieved memories that still lack a utility vote for one problem."""
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""This module defines lower-level retrieval interfaces for embedding and lexical operations."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Sequence
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IVectorSearch(ABC):
|
|
8
|
+
"""This interface defines vector embedding and similarity lookup capabilities."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def embed_query(self, text: str) -> Sequence[float]:
|
|
12
|
+
"""This method returns an embedding vector for query text."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IKeywordSearch(ABC):
|
|
16
|
+
"""This interface defines lexical lookup capabilities for query text."""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def normalize_query(self, text: str) -> str:
|
|
20
|
+
"""This method normalizes text before lexical retrieval."""
|