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.
Files changed (165) hide show
  1. app/__init__.py +1 -0
  2. app/__main__.py +7 -0
  3. app/boot/__init__.py +1 -0
  4. app/boot/admin_db.py +88 -0
  5. app/boot/config.py +14 -0
  6. app/boot/create_policy.py +52 -0
  7. app/boot/db.py +70 -0
  8. app/boot/embeddings.py +55 -0
  9. app/boot/home.py +45 -0
  10. app/boot/migrations.py +61 -0
  11. app/boot/read_policy.py +179 -0
  12. app/boot/repos.py +15 -0
  13. app/boot/retrieval.py +3 -0
  14. app/boot/thresholds.py +19 -0
  15. app/boot/update_policy.py +34 -0
  16. app/boot/use_cases.py +22 -0
  17. app/config/__init__.py +1 -0
  18. app/config/defaults/create_policy.yaml +7 -0
  19. app/config/defaults/read_policy.yaml +25 -0
  20. app/config/defaults/runtime.yaml +10 -0
  21. app/config/defaults/thresholds.yaml +3 -0
  22. app/config/defaults/update_policy.yaml +5 -0
  23. app/config/loader.py +58 -0
  24. app/core/__init__.py +1 -0
  25. app/core/contracts/__init__.py +1 -0
  26. app/core/contracts/errors.py +29 -0
  27. app/core/contracts/requests.py +211 -0
  28. app/core/contracts/responses.py +15 -0
  29. app/core/entities/__init__.py +1 -0
  30. app/core/entities/associations.py +58 -0
  31. app/core/entities/episodes.py +66 -0
  32. app/core/entities/evidence.py +29 -0
  33. app/core/entities/facts.py +30 -0
  34. app/core/entities/guidance.py +47 -0
  35. app/core/entities/identity.py +48 -0
  36. app/core/entities/memory.py +34 -0
  37. app/core/entities/runtime_context.py +19 -0
  38. app/core/entities/session_state.py +31 -0
  39. app/core/entities/telemetry.py +152 -0
  40. app/core/entities/utility.py +14 -0
  41. app/core/interfaces/__init__.py +1 -0
  42. app/core/interfaces/clock.py +12 -0
  43. app/core/interfaces/config.py +28 -0
  44. app/core/interfaces/embeddings.py +12 -0
  45. app/core/interfaces/idgen.py +11 -0
  46. app/core/interfaces/repos.py +279 -0
  47. app/core/interfaces/retrieval.py +20 -0
  48. app/core/interfaces/session_state_store.py +33 -0
  49. app/core/interfaces/unit_of_work.py +50 -0
  50. app/core/policies/__init__.py +1 -0
  51. app/core/policies/_shared/__init__.py +1 -0
  52. app/core/policies/_shared/executor.py +132 -0
  53. app/core/policies/_shared/side_effects.py +9 -0
  54. app/core/policies/create_policy/__init__.py +1 -0
  55. app/core/policies/create_policy/pipeline.py +96 -0
  56. app/core/policies/read_policy/__init__.py +1 -0
  57. app/core/policies/read_policy/bm25.py +114 -0
  58. app/core/policies/read_policy/context_pack_builder.py +140 -0
  59. app/core/policies/read_policy/expansion.py +132 -0
  60. app/core/policies/read_policy/fusion_rrf.py +34 -0
  61. app/core/policies/read_policy/lexical_query.py +101 -0
  62. app/core/policies/read_policy/pipeline.py +93 -0
  63. app/core/policies/read_policy/scenario_lift.py +11 -0
  64. app/core/policies/read_policy/scoring.py +61 -0
  65. app/core/policies/read_policy/seed_retrieval.py +54 -0
  66. app/core/policies/read_policy/utility_prior.py +11 -0
  67. app/core/policies/update_policy/__init__.py +1 -0
  68. app/core/policies/update_policy/pipeline.py +80 -0
  69. app/core/use_cases/__init__.py +1 -0
  70. app/core/use_cases/build_guidance.py +85 -0
  71. app/core/use_cases/create_memory.py +26 -0
  72. app/core/use_cases/manage_session_state.py +159 -0
  73. app/core/use_cases/read_memory.py +21 -0
  74. app/core/use_cases/record_episode_sync_telemetry.py +19 -0
  75. app/core/use_cases/record_operation_telemetry.py +32 -0
  76. app/core/use_cases/sync_episode.py +162 -0
  77. app/core/use_cases/update_memory.py +40 -0
  78. app/migrations/__init__.py +1 -0
  79. app/migrations/env.py +65 -0
  80. app/migrations/versions/20260226_0001_initial_schema.py +232 -0
  81. app/migrations/versions/20260312_0002_add_hard_invariants.py +60 -0
  82. app/migrations/versions/20260312_0003_drop_create_confidence.py +40 -0
  83. app/migrations/versions/20260313_0004_episode_sync_hardening.py +71 -0
  84. app/migrations/versions/20260313_0005_evidence_episode_event_refs.py +45 -0
  85. app/migrations/versions/20260318_0006_usage_telemetry_schema.py +175 -0
  86. app/migrations/versions/20260319_0007_identity_session_guidance.py +49 -0
  87. app/migrations/versions/20260320_0008_instance_metadata_and_backup_safety.py +31 -0
  88. app/migrations/versions/__init__.py +1 -0
  89. app/periphery/__init__.py +1 -0
  90. app/periphery/admin/__init__.py +1 -0
  91. app/periphery/admin/backup.py +360 -0
  92. app/periphery/admin/destructive_guard.py +32 -0
  93. app/periphery/admin/doctor.py +192 -0
  94. app/periphery/admin/init.py +996 -0
  95. app/periphery/admin/instance_guard.py +211 -0
  96. app/periphery/admin/machine_state.py +354 -0
  97. app/periphery/admin/privileges.py +42 -0
  98. app/periphery/admin/repo_state.py +266 -0
  99. app/periphery/admin/restore.py +30 -0
  100. app/periphery/cli/__init__.py +1 -0
  101. app/periphery/cli/handlers.py +830 -0
  102. app/periphery/cli/hydration.py +119 -0
  103. app/periphery/cli/main.py +710 -0
  104. app/periphery/cli/presenter_json.py +10 -0
  105. app/periphery/cli/schema_validation.py +201 -0
  106. app/periphery/db/__init__.py +1 -0
  107. app/periphery/db/engine.py +10 -0
  108. app/periphery/db/models/__init__.py +1 -0
  109. app/periphery/db/models/associations.py +55 -0
  110. app/periphery/db/models/episodes.py +55 -0
  111. app/periphery/db/models/evidence.py +19 -0
  112. app/periphery/db/models/experiences.py +33 -0
  113. app/periphery/db/models/instance_metadata.py +17 -0
  114. app/periphery/db/models/memories.py +39 -0
  115. app/periphery/db/models/metadata.py +6 -0
  116. app/periphery/db/models/registry.py +18 -0
  117. app/periphery/db/models/telemetry.py +174 -0
  118. app/periphery/db/models/utility.py +19 -0
  119. app/periphery/db/models/views.py +154 -0
  120. app/periphery/db/repos/__init__.py +1 -0
  121. app/periphery/db/repos/relational/__init__.py +1 -0
  122. app/periphery/db/repos/relational/associations_repo.py +117 -0
  123. app/periphery/db/repos/relational/episodes_repo.py +188 -0
  124. app/periphery/db/repos/relational/evidence_repo.py +82 -0
  125. app/periphery/db/repos/relational/experiences_repo.py +41 -0
  126. app/periphery/db/repos/relational/memories_repo.py +99 -0
  127. app/periphery/db/repos/relational/read_policy_repo.py +202 -0
  128. app/periphery/db/repos/relational/telemetry_repo.py +161 -0
  129. app/periphery/db/repos/relational/utility_repo.py +30 -0
  130. app/periphery/db/repos/semantic/__init__.py +1 -0
  131. app/periphery/db/repos/semantic/keyword_retrieval_repo.py +63 -0
  132. app/periphery/db/repos/semantic/semantic_retrieval_repo.py +111 -0
  133. app/periphery/db/session.py +10 -0
  134. app/periphery/db/uow.py +75 -0
  135. app/periphery/embeddings/__init__.py +1 -0
  136. app/periphery/embeddings/local_provider.py +35 -0
  137. app/periphery/embeddings/query_vector_search.py +18 -0
  138. app/periphery/episodes/__init__.py +1 -0
  139. app/periphery/episodes/claude_code.py +387 -0
  140. app/periphery/episodes/codex.py +423 -0
  141. app/periphery/episodes/launcher.py +66 -0
  142. app/periphery/episodes/normalization.py +31 -0
  143. app/periphery/episodes/poller.py +299 -0
  144. app/periphery/episodes/source_discovery.py +66 -0
  145. app/periphery/episodes/tool_filter.py +165 -0
  146. app/periphery/identity/__init__.py +1 -0
  147. app/periphery/identity/claude_hook_install.py +67 -0
  148. app/periphery/identity/claude_runtime.py +83 -0
  149. app/periphery/identity/codex_runtime.py +32 -0
  150. app/periphery/identity/compatibility.py +38 -0
  151. app/periphery/identity/resolver.py +163 -0
  152. app/periphery/session_state/__init__.py +1 -0
  153. app/periphery/session_state/file_store.py +100 -0
  154. app/periphery/telemetry/__init__.py +33 -0
  155. app/periphery/telemetry/operation_summary.py +299 -0
  156. app/periphery/telemetry/session_selection.py +156 -0
  157. app/periphery/telemetry/sync_summary.py +65 -0
  158. app/periphery/validation/__init__.py +1 -0
  159. app/periphery/validation/integrity_validation.py +253 -0
  160. app/periphery/validation/semantic_validation.py +94 -0
  161. shellbrain-0.1.0.dist-info/METADATA +130 -0
  162. shellbrain-0.1.0.dist-info/RECORD +165 -0
  163. shellbrain-0.1.0.dist-info/WHEEL +5 -0
  164. shellbrain-0.1.0.dist-info/entry_points.txt +2 -0
  165. 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."""