claude-smart 0.2.47 → 0.2.48

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 (113) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/.codex-plugin/plugin.json +1 -1
  6. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  7. package/plugin/dashboard/package-lock.json +61 -390
  8. package/plugin/dashboard/package.json +2 -2
  9. package/plugin/pyproject.toml +1 -1
  10. package/plugin/uv.lock +1 -1
  11. package/plugin/vendor/reflexio/.env.example +7 -0
  12. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  13. package/plugin/vendor/reflexio/reflexio/README.md +8 -5
  14. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  15. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  16. package/plugin/vendor/reflexio/reflexio/lib/_search.py +15 -11
  17. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  18. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +24 -3
  19. package/plugin/vendor/reflexio/reflexio/server/README.md +25 -8
  20. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  21. package/plugin/vendor/reflexio/reflexio/server/api.py +133 -3273
  22. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  23. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  24. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  25. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  26. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  27. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  28. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  29. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  30. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  31. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  32. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  33. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +56 -4
  34. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  35. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  36. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  37. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  38. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  39. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  40. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  41. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  42. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  43. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  44. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  45. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  46. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  47. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  48. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  49. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  50. package/plugin/vendor/reflexio/reflexio/server/services/README.md +3 -2
  51. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  52. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  53. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  54. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  55. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  56. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  57. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  58. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  59. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  60. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  61. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  62. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +362 -81
  63. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -490
  64. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  65. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  66. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  67. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  68. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  69. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +29 -4
  70. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  71. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +56 -351
  72. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +0 -1513
  73. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +6 -1
  74. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1281
  75. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  76. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  77. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  79. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  87. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  88. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  89. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +6 -3
  90. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +13 -7
  91. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  95. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +33 -8
  96. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  97. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  98. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  102. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  103. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  104. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +44 -86
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +0 -148
@@ -9,6 +9,7 @@
9
9
  "lint": "eslint"
10
10
  },
11
11
  "dependencies": {
12
+ "@tailwindcss/postcss": "^4",
12
13
  "@base-ui/react": "^1.3.0",
13
14
  "class-variance-authority": "^0.7.1",
14
15
  "clsx": "^2.1.1",
@@ -17,17 +18,16 @@
17
18
  "next-themes": "^0.4.6",
18
19
  "react": "19.2.4",
19
20
  "react-dom": "19.2.4",
21
+ "shadcn": "^4.0.8",
20
22
  "tailwind-merge": "^3.5.0",
21
23
  "tw-animate-css": "^1.4.0"
22
24
  },
23
25
  "devDependencies": {
24
- "@tailwindcss/postcss": "^4",
25
26
  "@types/node": "^20",
26
27
  "@types/react": "^19",
27
28
  "@types/react-dom": "^19",
28
29
  "eslint": "^9",
29
30
  "eslint-config-next": "16.2.6",
30
- "shadcn": "^4.0.8",
31
31
  "tailwindcss": "^4",
32
32
  "typescript": "^5"
33
33
  },
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-smart"
3
- version = "0.2.47"
3
+ version = "0.2.48"
4
4
  description = "Self-improving Claude Code, Codex, and OpenCode plugin that turns corrections into Preferences, Project-specific skills, and Shared skills"
5
5
  keywords = [
6
6
  "claude",
package/plugin/uv.lock CHANGED
@@ -476,7 +476,7 @@ wheels = [
476
476
 
477
477
  [[package]]
478
478
  name = "claude-smart"
479
- version = "0.2.47"
479
+ version = "0.2.48"
480
480
  source = { editable = "." }
481
481
  dependencies = [
482
482
  { name = "chromadb" },
@@ -67,6 +67,13 @@ REFLEXIO_DEFAULT_ORG_ID=self-host-org
67
67
  # elided. (optional; default 512; set <= 0 to disable slicing and pass full
68
68
  # content)
69
69
  REFLEXIO_MAX_INTERACTION_CONTENT_TOKENS=512
70
+ # Warn when queued post-persist async publish learning jobs reach this depth.
71
+ # The queue is unbounded so acknowledged publishes do not drop learning due to
72
+ # transient limiter pressure. (optional; default 1000)
73
+ REFLEXIO_PUBLISH_LEARNING_QUEUE_WARN_SIZE=1000
74
+ # Worker threads for post-persist async publish learning. (optional; default
75
+ # matches the configured publish concurrency limit, currently 4)
76
+ REFLEXIO_PUBLISH_LEARNING_WORKERS=
70
77
  # Inactivity delay in seconds before a quiet session is evaluated by agent-success
71
78
  # evaluation. (optional; default 600 = 10 minutes)
72
79
  GROUP_EVALUATION_DELAY_SECONDS=600
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reflexio-ai"
3
- version = "0.2.27"
3
+ version = "0.2.28"
4
4
  description = "A Python library for the Reflexio"
5
5
  authors = [{name = "Reflexio Team"}]
6
6
  readme = "README.md"
@@ -218,6 +218,7 @@ ignore = [
218
218
  "reflexio/scripts/**" = ["S101", "G004", "ANN"]
219
219
  "reflexio/benchmarks/**" = ["S311"]
220
220
  "reflexio/server/api.py" = ["B008", "ARG001", "ARG002"]
221
+ "reflexio/server/routes/**" = ["B008", "ARG001", "ARG002"]
221
222
  "reflexio/server/api_endpoints/**" = ["B008", "ARG001", "ARG002"]
222
223
  "reflexio/integrations/**" = ["ARG002"]
223
224
  # The openclaw plugin is a nested package; its tests/scripts/cli are not reached
@@ -101,9 +101,10 @@ Description: FastAPI backend server that processes user interactions to generate
101
101
  **Detailed Documentation**: See [`reflexio/server/README.md`](server/README.md) for component details, including the [Prompt Bank](server/prompt/prompt_bank/README.md), [Playbook Service](server/services/playbook/README.md), and [Site Variables](server/site_var/README.md)
102
102
 
103
103
  ### Main Entry Points
104
- - **API**: `api.py` - FastAPI routes
104
+ - **API composer**: `api.py` - `create_app()` factory, middleware/capability wiring, and `core_router` aggregation
105
+ - **Domain routes**: `server/routes/` - FastAPI route modules grouped by system, interactions, profiles, playbooks, search, provenance, evaluation, Braintrust, and config
105
106
  - **Extension Registry**: `extensions.py` - optional capability/service registration for OSS and enterprise integrations
106
- - **Endpoint Helpers**: `api_endpoints/` - Request handlers calling `Reflexio` (reflexio_lib)
107
+ - **Endpoint Helpers**: `api_endpoints/` - Shared handler/helper functions and `RequestContext` used by route modules
107
108
  - **Core Service**: `services/generation_service.py` - Main orchestrator
108
109
 
109
110
  ### Purpose
@@ -115,8 +116,9 @@ Receives user interactions from clients and processes them to:
115
116
  ### Component Relationships
116
117
  ```
117
118
  client (Python SDK)
118
- -> api.py (FastAPI routes)
119
- -> api_endpoints/ (request handlers)
119
+ -> api.py (create_app + core_router)
120
+ -> routes/ (domain FastAPI routers)
121
+ -> api_endpoints/ (shared handlers + RequestContext)
120
122
  -> reflexio_lib.Reflexio (main entry)
121
123
  -> services/generation_service.py (orchestrator)
122
124
  ├─> services/profile/ -> storage (BaseStorage)
@@ -126,12 +128,13 @@ client (Python SDK)
126
128
 
127
129
  ### Key Components
128
130
  - **`api_endpoints/`**: Request handling, `RequestContext` (bundles storage/config/prompts), auth
131
+ - **`routes/`**: Domain route modules (`system.py`, `interactions.py`, `profiles.py`, `playbooks.py`, `search.py`, `provenance.py`, `evaluation.py`, `braintrust.py`, `config.py`) included into `api.py`'s `core_router`
129
132
  - **`db/`**: Auth & config storage only (SQLite) - NOT for profiles/interactions
130
133
  - **`llm/`**: Unified LLM client (auto-detects OpenAI/Claude from model name)
131
134
  - **`prompt/`**: Versioned prompt templates in `prompt_bank/`
132
135
  - **`services/`**: Core business logic
133
136
  - `generation_service.py` - Orchestrator (runs profile/playbook/success services)
134
- - `base_generation_service.py` - Abstract base for parallel actor execution
137
+ - `base_generation_service.py` + `base_generation/` - Abstract base plus mixins for parallel actor execution, batch progress, should-run prechecks, status transitions, and usage billing
135
138
  - `profile/` - Profile extraction & updates
136
139
  - `playbook/` - Playbook extraction, consolidation, and aggregation
137
140
  - `agent_success_evaluation/` - Success evaluation
@@ -29,32 +29,37 @@ class ConfigMixin(ReflexioBase):
29
29
  # security). Without this, the marker reaches the readiness check as
30
30
  # an unfillable StorageConfigManagedSupabase and fails with
31
31
  # "Storage configuration is incomplete".
32
+ current_storage_config = configurator.get_current_storage_configuration()
32
33
  storage_config = config.storage_config
33
34
  if storage_config is None or isinstance(
34
35
  storage_config, StorageConfigManagedSupabase
35
36
  ):
36
- storage_config = configurator.get_current_storage_configuration()
37
+ storage_config = current_storage_config
37
38
  config.storage_config = storage_config
38
39
 
39
- # Check if storage config is ready to test
40
- if not configurator.is_storage_config_ready_to_test(
41
- storage_config=storage_config
42
- ):
43
- return SetConfigResponse(
44
- success=False, msg="Storage configuration is incomplete"
40
+ storage_config_changed = storage_config != current_storage_config
41
+ if storage_config_changed or current_storage_config is None:
42
+ # Storage validation initializes the backend and may run
43
+ # migrations, so only do it when the storage target changes.
44
+ if not configurator.is_storage_config_ready_to_test(
45
+ storage_config=storage_config
46
+ ):
47
+ return SetConfigResponse(
48
+ success=False, msg="Storage configuration is incomplete"
49
+ )
50
+
51
+ (
52
+ success,
53
+ error_msg,
54
+ ) = configurator.test_and_init_storage_config(
55
+ storage_config=storage_config
45
56
  )
46
57
 
47
- # Test and initialize storage connection
48
- (
49
- success,
50
- error_msg,
51
- ) = configurator.test_and_init_storage_config(storage_config=storage_config)
52
-
53
- if not success:
54
- return SetConfigResponse(
55
- success=False,
56
- msg=f"Failed to validate storage connection: {error_msg}",
57
- )
58
+ if not success:
59
+ return SetConfigResponse(
60
+ success=False,
61
+ msg=f"Failed to validate storage connection: {error_msg}",
62
+ )
58
63
 
59
64
  # Only set config if validation passed
60
65
  configurator.set_config(config)
@@ -53,11 +53,21 @@ class InteractionsMixin(ReflexioBase):
53
53
  def publish_interaction(
54
54
  self,
55
55
  request: PublishUserInteractionRequest | dict,
56
+ *,
57
+ use_publish_limiter: bool = True,
58
+ publish_limiter_wait_forever: bool = True,
59
+ defer_learning: bool = False,
56
60
  ) -> PublishUserInteractionResponse:
57
61
  """Publish user interactions.
58
62
 
59
63
  Args:
60
64
  request (Union[PublishUserInteractionRequest, dict]): The publish user interaction request
65
+ use_publish_limiter: Whether GenerationService should throttle the
66
+ post-write learning pipeline.
67
+ publish_limiter_wait_forever: Whether GenerationService should queue
68
+ indefinitely for that post-write learning limiter.
69
+ defer_learning: Whether to enqueue post-persist learning instead of
70
+ running it inline.
61
71
 
62
72
  Returns:
63
73
  PublishUserInteractionResponse: Response containing success status and message
@@ -87,7 +97,12 @@ class InteractionsMixin(ReflexioBase):
87
97
  # Convert dict to PublishUserInteractionRequest if needed
88
98
  if isinstance(request, dict):
89
99
  request = PublishUserInteractionRequest(**request)
90
- result = generation_service.run(request)
100
+ result = generation_service.run(
101
+ request,
102
+ use_publish_limiter=use_publish_limiter,
103
+ publish_limiter_wait_forever=publish_limiter_wait_forever,
104
+ defer_learning=defer_learning,
105
+ )
91
106
  after_profiles = _safe_count(storage.count_all_profiles)
92
107
  after_playbooks = _safe_count(storage.count_user_playbooks)
93
108
  # Don't concatenate warnings into the message field — they
@@ -42,17 +42,21 @@ class SearchMixin(ReflexioBase):
42
42
 
43
43
  try:
44
44
  if request.start_time or request.end_time:
45
- results = self._get_storage().get_agent_success_evaluation_results_in_window(
46
- from_ts=(
47
- int(request.start_time.timestamp()) if request.start_time else 0
48
- ),
49
- to_ts=(
50
- int(request.end_time.timestamp())
51
- if request.end_time
52
- else 2**31 - 1
53
- ),
54
- limit=request.limit or 100,
55
- agent_version=request.agent_version,
45
+ results = (
46
+ self._get_storage().get_agent_success_evaluation_results_in_window(
47
+ from_ts=(
48
+ int(request.start_time.timestamp())
49
+ if request.start_time
50
+ else 0
51
+ ),
52
+ to_ts=(
53
+ int(request.end_time.timestamp())
54
+ if request.end_time
55
+ else 2**31 - 1
56
+ ),
57
+ limit=request.limit or 100,
58
+ agent_version=request.agent_version,
59
+ )
56
60
  )
57
61
  else:
58
62
  results = self._get_storage().get_agent_success_evaluation_results(
@@ -46,6 +46,7 @@ class Status(str, Enum): # noqa: UP042 - CURRENT=None is not compatible with St
46
46
  SUPERSEDED = (
47
47
  "superseded" # tombstone: replaced by a new version (superseded_by set)
48
48
  )
49
+ EXPIRED = "expired" # tombstone: TTL elapsed while active (see expiry reclamation)
49
50
 
50
51
 
51
52
  class OperationStatus(StrEnum):
@@ -708,11 +708,27 @@ class LineageGCConfig(BaseModel):
708
708
  poll_interval_seconds: int = Field(default=86400, gt=0)
709
709
 
710
710
 
711
+ class ExpiryReclamationConfig(BaseModel):
712
+ """Direct-delete reclamation of expired plain rows (non-audited).
713
+
714
+ Independent of ``lineage_gc``: these rows carry no PII/audit/grace obligation,
715
+ so they can be reclaimed whenever this is enabled even if tombstone GC is off.
716
+
717
+ Opt-in by default (``enabled=False``) so operators control a staged rollout
718
+ of the direct-delete Class B sweeps and are not surprised by deletions on
719
+ upgrade. ``lineage_gc.enabled`` (which defaults to True) is unaffected and
720
+ continues to drive the Class A profile-expiry and tombstone-GC paths.
721
+ """
722
+
723
+ enabled: bool = False
724
+
725
+
711
726
  class GovernanceRetentionConfig(BaseModel):
712
727
  """Audit-event retention policy. **Enterprise-only:** reclamation is performed
713
- by the reflexio_ext GovernanceRetentionCapability. In an OSS-only deployment
714
- these knobs are accepted but inert (the OSS lineage scheduler does not reclaim
715
- audit events) and the server logs a startup warning when retention is enabled.
728
+ by an enterprise per-org reclamation sweep registered via
729
+ ``register_per_org_sweep``. In an OSS-only deployment these knobs are accepted
730
+ but inert (the OSS lineage scheduler does not reclaim audit events) and the
731
+ server logs a startup warning when retention is enabled.
716
732
  """
717
733
 
718
734
  audit_events_retention_enabled: bool = False
@@ -859,6 +875,10 @@ class Config(BaseModel):
859
875
  )
860
876
  # Tombstone GC job gate (opt-in, off by default — see LineageGCConfig)
861
877
  lineage_gc: LineageGCConfig = Field(default_factory=LineageGCConfig)
878
+ # Direct-delete reclamation of expired plain rows (share links, pending tool calls)
879
+ expiry_reclamation: ExpiryReclamationConfig = Field(
880
+ default_factory=ExpiryReclamationConfig
881
+ )
862
882
  governance_retention: GovernanceRetentionConfig = Field(
863
883
  default_factory=GovernanceRetentionConfig
864
884
  )
@@ -920,6 +940,7 @@ class Config(BaseModel):
920
940
  "reflection_config",
921
941
  "playbook_optimizer_config",
922
942
  "lineage_gc",
943
+ "expiry_reclamation",
923
944
  "governance_retention",
924
945
  "pending_tool_call_config",
925
946
  "retrieval_floor",
@@ -33,8 +33,9 @@ Description: FastAPI backend server that processes user interactions to generate
33
33
 
34
34
  ## Main Entry Points
35
35
 
36
- - **API**: `api.py` - FastAPI routes (only place to expose endpoints)
37
- - **Endpoint Helpers**: `api_endpoints/` - Bridge between routes and business logic
36
+ - **API composer**: `api.py` - `create_app()` factory, middleware/capability wiring, OpenAPI auth decoration, and `core_router` aggregation
37
+ - **Domain routes**: `routes/` - FastAPI route modules; add new public API surfaces here and include their routers in `api.py`
38
+ - **Endpoint Helpers**: `api_endpoints/` - Shared handlers/helpers plus `RequestContext` used by route modules
38
39
  - **Extension Registry**: `extensions.py` - Capability and service registry for optional OSS/enterprise integrations
39
40
  - **Core Service**: `services/generation_service.py` - Main orchestrator
40
41
 
@@ -57,7 +58,19 @@ Description: FastAPI backend server that processes user interactions to generate
57
58
 
58
59
  **Directory**: `api_endpoints/`
59
60
 
60
- **Detailed Documentation**: See [`api_endpoints/README.md`](api_endpoints/README.md) for the `RequestContext` contract and per-file handler map.
61
+ **Route modules** live in `routes/` and are grouped by domain. `api.py` remains the composition root: it creates `core_router`, includes each domain router, and mounts the aggregate router into the FastAPI app. **Detailed handler documentation**: See [`api_endpoints/README.md`](api_endpoints/README.md) for the `RequestContext` contract and helper map.
62
+
63
+ | Route file | Purpose |
64
+ |------|---------|
65
+ | `routes/system.py` | Root/health/version and operation status/cancel surfaces. |
66
+ | `routes/interactions.py` | Publish, request/session/interaction retrieval, direct interaction writes, and clear-data operations. |
67
+ | `routes/profiles.py` | Profile retrieval, statistics, rerun/manual generation, upgrade/downgrade, update/delete lifecycle routes. |
68
+ | `routes/playbooks.py` | User/agent playbook retrieval, aggregation, lifecycle, status/update/delete, and application stats routes. |
69
+ | `routes/search.py` | Unified search and entity-specific search/rerank routes. |
70
+ | `routes/provenance.py` | Learning provenance and approval routes. |
71
+ | `routes/evaluation.py` | Evaluation overview, regenerate jobs, grade-on-demand, shadow comparisons, pending tool calls, and stall-state routes. |
72
+ | `routes/braintrust.py` | Braintrust connection/project/status/sync routes. |
73
+ | `routes/config.py` | Config read/write and account identity routes. |
61
74
 
62
75
  | File | Purpose |
63
76
  |------|---------|
@@ -97,10 +110,13 @@ Description: FastAPI backend server that processes user interactions to generate
97
110
  ## LLM Client
98
111
 
99
112
  **Directory**: `llm/`
100
- **Entry Point**: `litellm_client.py` - `LiteLLMClient`
113
+ **Entry Point**: `litellm_client.py` - `LiteLLMClient` facade composed from focused mixins
101
114
 
102
115
  Key files:
103
- - `litellm_client.py`: Unified LiteLLMClient using LiteLLM for multi-provider support
116
+ - `litellm_client.py`: Stable import surface, client config/credential resolution, and `LiteLLMClient` facade
117
+ - `_litellm_text_generation.py`, `_litellm_embedding.py`, `_litellm_structured_output.py`: Completion/tool-call, embedding, and structured-output mixins
118
+ - `_litellm_json_extraction.py`, `_litellm_subprocess.py`, `_litellm_types.py`: JSON parsing, hard-timeout subprocess snapshots/workers, and shared public types/errors
119
+ - `providers/`: Optional local/provider adapters (`claude-code/`, OpenClaw, local embedding, Nomic embedding); registration is opt-in via environment/config
104
120
  - `openai_client.py`: OpenAI implementation (legacy, do not use directly)
105
121
  - `claude_client.py`: Claude implementation (legacy, do not use directly)
106
122
  - `llm_utils.py`: Helper functions for Pydantic model conversion
@@ -230,7 +246,8 @@ Called by API endpoints via `Reflexio`
230
246
 
231
247
  ### Base Infrastructure
232
248
 
233
- - `base_generation_service.py`: Abstract base for all services (parallel extractor execution via ThreadPoolExecutor, `EXTRACTOR_TIMEOUT_SECONDS = 300` per-extractor safety timeout)
249
+ - `base_generation_service.py`: Stable `BaseGenerationService` import surface plus service-specific orchestration hooks (parallel extractor execution via ThreadPoolExecutor, `EXTRACTOR_TIMEOUT_SECONDS = 300` per-extractor safety timeout)
250
+ - `base_generation/`: Mixins for batch progress, config filtering, extraction lifecycle, should-run prechecks, status transitions, and usage billing that keep `base_generation_service.py` navigable without changing caller imports
234
251
  - `extractor_config_utils.py`: Shared utility for filtering extractor configs by source, `allow_manual_trigger`, and extractor names
235
252
  - `extractor_interaction_utils.py`: Per-extractor utilities for stride_size checking and source filtering
236
253
  - `operation_state_utils.py`: Centralized `OperationStateManager` for all `_operation_state` table interactions (progress tracking, concurrency locks, extractor/aggregator bookmarks, simple locks)
@@ -488,8 +505,8 @@ Pre-computed embeddings passed to storage methods via `query_embedding` paramete
488
505
 
489
506
  | File | Purpose |
490
507
  |------|---------|
491
- | `storage_base/` | BaseStorage interface split by domain (`_profiles.py`, `_playbook.py`, `_requests.py`, `_operations.py`, `_agent_run.py`, `_lineage.py`, `_shadow_verdicts.py`, `_stall_state.py`, `_share_links.py`) |
492
- | `sqlite_storage/` | SQLite-backed implementation split across the same domains, including governance-aware retention/barrier handling and lineage/tombstone support in `_governance.py` / `_lineage.py` |
508
+ | `storage_base/` | BaseStorage interface split by domain. Legacy facades (`_profiles.py`, `_playbook.py`, `_agent_run.py`, etc.) preserve imports while subpackages (`profiles/`, `playbook/`, `agent_run/`, `governance/`) hold focused abstract store contracts. |
509
+ | `sqlite_storage/` | SQLite-backed implementation split across matching facades and subpackages (`profiles/`, `playbook/`, `agent_run/`, `governance/`, `base/`), including governance-aware retention/barrier handling and lineage/tombstone support. |
493
510
  | `governance_validation.py` | Shared validation helpers for subject references and governance contracts before storage writes. |
494
511
  | `retention.py`, `retention_mixin.py` | Data retention and cleanup helpers |
495
512
  | `constants.py`, `error.py` | Storage constants and shared errors |
@@ -230,5 +230,24 @@ if DEBUG_LOG_TO_CONSOLE:
230
230
  logging.ERROR
231
231
  )
232
232
  else:
233
- # Default to WARNING level when DEBUG_LOG_TO_CONSOLE is not set or is false
234
- root_logger.setLevel(logging.WARNING)
233
+ # Production (DEBUG_LOG_TO_CONSOLE off): emit INFO+ app logs to stdout so the
234
+ # container log driver (e.g. CloudWatch awslogs) captures business-logic audit
235
+ # lines — billing/webhook/enforcement, etc. Without an explicit handler the
236
+ # stdlib "handler of last resort" emits only WARNING+ to stderr, so INFO logs
237
+ # were silently dropped in production. Plain formatter (no colors/files/dedup)
238
+ # keeps this lean and log-driver friendly — none of the DEBUG-branch overhead.
239
+ root_logger.setLevel(logging.INFO)
240
+ if not any(isinstance(h, logging.StreamHandler) for h in root_logger.handlers):
241
+ prod_console_handler = logging.StreamHandler(sys.stdout)
242
+ prod_console_handler.setLevel(logging.INFO)
243
+ prod_console_handler.setFormatter(
244
+ logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
245
+ )
246
+ root_logger.addHandler(prod_console_handler)
247
+
248
+ # Keep noisy loggers quiet even at INFO (mirror the debug branch's suppression).
249
+ for _noisy in ("litellm", "LiteLLM", "httpx", "httpcore", "openai", "urllib3"):
250
+ logging.getLogger(_noisy).setLevel(logging.WARNING)
251
+ logging.getLogger("reflexio.server.site_var.site_var_manager").setLevel(
252
+ logging.ERROR
253
+ )