shellbrain 0.1.25__tar.gz → 0.1.26__tar.gz

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 (225) hide show
  1. {shellbrain-0.1.25 → shellbrain-0.1.26}/PKG-INFO +1 -1
  2. shellbrain-0.1.26/app/migrations/versions/20260422_0015_problem_runs.py +101 -0
  3. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/main.py +34 -50
  4. shellbrain-0.1.26/app/periphery/db/models/problem_runs.py +60 -0
  5. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/registry.py +2 -1
  6. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/views.py +212 -0
  7. shellbrain-0.1.26/app/periphery/metrics/pager.py +250 -0
  8. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/queries.py +23 -0
  9. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/service.py +8 -0
  10. {shellbrain-0.1.25 → shellbrain-0.1.26}/pyproject.toml +1 -1
  11. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/PKG-INFO +1 -1
  12. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/SOURCES.txt +3 -0
  13. {shellbrain-0.1.25 → shellbrain-0.1.26}/README.md +0 -0
  14. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/__init__.py +0 -0
  15. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/__main__.py +0 -0
  16. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/__init__.py +0 -0
  17. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/_dsn_resolution.py +0 -0
  18. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/admin_db.py +0 -0
  19. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/config.py +0 -0
  20. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/create_policy.py +0 -0
  21. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/db.py +0 -0
  22. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/embeddings.py +0 -0
  23. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/home.py +0 -0
  24. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/migrations.py +0 -0
  25. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/read_policy.py +0 -0
  26. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/repos.py +0 -0
  27. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/retrieval.py +0 -0
  28. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/thresholds.py +0 -0
  29. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/update_policy.py +0 -0
  30. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/boot/use_cases.py +0 -0
  31. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/__init__.py +0 -0
  32. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/defaults/create_policy.yaml +0 -0
  33. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/defaults/read_policy.yaml +0 -0
  34. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/defaults/runtime.yaml +0 -0
  35. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/defaults/thresholds.yaml +0 -0
  36. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/defaults/update_policy.yaml +0 -0
  37. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/config/loader.py +0 -0
  38. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/__init__.py +0 -0
  39. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/contracts/__init__.py +0 -0
  40. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/contracts/concepts.py +0 -0
  41. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/contracts/errors.py +0 -0
  42. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/contracts/requests.py +0 -0
  43. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/contracts/responses.py +0 -0
  44. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/__init__.py +0 -0
  45. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/associations.py +0 -0
  46. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/concepts.py +0 -0
  47. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/episodes.py +0 -0
  48. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/evidence.py +0 -0
  49. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/facts.py +0 -0
  50. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/guidance.py +0 -0
  51. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/identity.py +0 -0
  52. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/memory.py +0 -0
  53. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/runtime_context.py +0 -0
  54. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/session_state.py +0 -0
  55. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/telemetry.py +0 -0
  56. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/entities/utility.py +0 -0
  57. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/__init__.py +0 -0
  58. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/clock.py +0 -0
  59. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/config.py +0 -0
  60. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/embeddings.py +0 -0
  61. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/idgen.py +0 -0
  62. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/repos.py +0 -0
  63. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/retrieval.py +0 -0
  64. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/session_state_store.py +0 -0
  65. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/interfaces/unit_of_work.py +0 -0
  66. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/__init__.py +0 -0
  67. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/_shared/__init__.py +0 -0
  68. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/_shared/executor.py +0 -0
  69. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/_shared/side_effects.py +0 -0
  70. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/create_policy/__init__.py +0 -0
  71. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/create_policy/pipeline.py +0 -0
  72. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/__init__.py +0 -0
  73. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/bm25.py +0 -0
  74. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/context_pack_builder.py +0 -0
  75. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/expansion.py +0 -0
  76. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/fusion_rrf.py +0 -0
  77. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/lexical_query.py +0 -0
  78. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/pipeline.py +0 -0
  79. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/scoring.py +0 -0
  80. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/read_policy/seed_retrieval.py +0 -0
  81. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/update_policy/__init__.py +0 -0
  82. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/policies/update_policy/pipeline.py +0 -0
  83. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/__init__.py +0 -0
  84. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/build_guidance.py +0 -0
  85. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/create_memory.py +0 -0
  86. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/manage_concepts.py +0 -0
  87. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/manage_session_state.py +0 -0
  88. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/read_concepts.py +0 -0
  89. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/read_memory.py +0 -0
  90. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/record_episode_sync_telemetry.py +0 -0
  91. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/record_model_usage_telemetry.py +0 -0
  92. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/record_operation_telemetry.py +0 -0
  93. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/sync_episode.py +0 -0
  94. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/core/use_cases/update_memory.py +0 -0
  95. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/__init__.py +0 -0
  96. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/env.py +0 -0
  97. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260226_0001_initial_schema.py +0 -0
  98. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260312_0002_add_hard_invariants.py +0 -0
  99. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260312_0003_drop_create_confidence.py +0 -0
  100. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260313_0004_episode_sync_hardening.py +0 -0
  101. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260313_0005_evidence_episode_event_refs.py +0 -0
  102. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260318_0006_usage_telemetry_schema.py +0 -0
  103. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260319_0007_identity_session_guidance.py +0 -0
  104. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260320_0008_instance_metadata_and_backup_safety.py +0 -0
  105. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260410_0009_frontier_memory_family.py +0 -0
  106. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260414_0010_model_usage_telemetry.py +0 -0
  107. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260414_0011_usage_problem_tokens_multi_solution_metrics.py +0 -0
  108. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260415_0012_read_pack_cost_and_read_roi.py +0 -0
  109. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260421_0013_concept_context_graph.py +0 -0
  110. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/20260422_0014_concept_read_telemetry.py +0 -0
  111. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/migrations/versions/__init__.py +0 -0
  112. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/__init__.py +0 -0
  113. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/claude/CLAUDE.md +0 -0
  114. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/claude/skills/shellbrain-session-start/SKILL.md +0 -0
  115. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/claude/skills/shellbrain-usage-review/SKILL.md +0 -0
  116. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/AGENTS.md +0 -0
  117. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/SKILL.md +0 -0
  118. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/agents/openai.yaml +0 -0
  119. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain-large.svg +0 -0
  120. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain-small.svg +0 -0
  121. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain_logo.png +0 -0
  122. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/references/request-shapes.md +0 -0
  123. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-session-start/references/session-workflow.md +0 -0
  124. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-usage-review/SKILL.md +0 -0
  125. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-usage-review/agents/openai.yaml +0 -0
  126. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-usage-review/assets/shellbrain-small.svg +0 -0
  127. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/codex/shellbrain-usage-review/assets/shellbrain_logo.png +0 -0
  128. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/cursor/skills/shellbrain-session-start/SKILL.md +0 -0
  129. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/onboarding_assets/cursor/skills/shellbrain-usage-review/SKILL.md +0 -0
  130. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/__init__.py +0 -0
  131. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/__init__.py +0 -0
  132. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/agent_behavior_analysis.py +0 -0
  133. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/analytics.py +0 -0
  134. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/analytics_diagnostics.py +0 -0
  135. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/analytics_queries.py +0 -0
  136. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/backup.py +0 -0
  137. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/destructive_guard.py +0 -0
  138. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/doctor.py +0 -0
  139. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/external_runtime.py +0 -0
  140. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/init.py +0 -0
  141. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/init_errors.py +0 -0
  142. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/instance_guard.py +0 -0
  143. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/machine_state.py +0 -0
  144. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/managed_runtime.py +0 -0
  145. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/model_usage_backfill.py +0 -0
  146. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/privileges.py +0 -0
  147. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/repo_state.py +0 -0
  148. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/restore.py +0 -0
  149. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/storage_setup.py +0 -0
  150. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/admin/upgrade.py +0 -0
  151. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/__init__.py +0 -0
  152. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/handlers.py +0 -0
  153. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/hydration.py +0 -0
  154. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/presenter_json.py +0 -0
  155. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/cli/schema_validation.py +0 -0
  156. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/__init__.py +0 -0
  157. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/engine.py +0 -0
  158. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/__init__.py +0 -0
  159. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/associations.py +0 -0
  160. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/concepts.py +0 -0
  161. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/episodes.py +0 -0
  162. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/evidence.py +0 -0
  163. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/experiences.py +0 -0
  164. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/instance_metadata.py +0 -0
  165. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/memories.py +0 -0
  166. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/metadata.py +0 -0
  167. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/telemetry.py +0 -0
  168. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/models/utility.py +0 -0
  169. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/__init__.py +0 -0
  170. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/__init__.py +0 -0
  171. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/associations_repo.py +0 -0
  172. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/concepts_repo.py +0 -0
  173. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/episodes_repo.py +0 -0
  174. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/evidence_repo.py +0 -0
  175. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/experiences_repo.py +0 -0
  176. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/memories_repo.py +0 -0
  177. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/read_policy_repo.py +0 -0
  178. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/telemetry_repo.py +0 -0
  179. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/relational/utility_repo.py +0 -0
  180. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/semantic/__init__.py +0 -0
  181. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/semantic/keyword_retrieval_repo.py +0 -0
  182. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/repos/semantic/semantic_retrieval_repo.py +0 -0
  183. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/session.py +0 -0
  184. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/db/uow.py +0 -0
  185. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/embeddings/__init__.py +0 -0
  186. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/embeddings/local_provider.py +0 -0
  187. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/embeddings/query_vector_search.py +0 -0
  188. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/__init__.py +0 -0
  189. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/claude_code.py +0 -0
  190. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/codex.py +0 -0
  191. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/cursor.py +0 -0
  192. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/launcher.py +0 -0
  193. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/model_usage.py +0 -0
  194. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/normalization.py +0 -0
  195. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/poller.py +0 -0
  196. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/poller_lock.py +0 -0
  197. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/source_discovery.py +0 -0
  198. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/episodes/tool_filter.py +0 -0
  199. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/__init__.py +0 -0
  200. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/claude_hook_install.py +0 -0
  201. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/claude_runtime.py +0 -0
  202. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/codex_runtime.py +0 -0
  203. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/compatibility.py +0 -0
  204. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/cursor_statusline.py +0 -0
  205. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/identity/resolver.py +0 -0
  206. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/__init__.py +0 -0
  207. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/artifacts.py +0 -0
  208. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/browser.py +0 -0
  209. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/metrics/render_html.py +0 -0
  210. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/onboarding/__init__.py +0 -0
  211. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/onboarding/host_assets.py +0 -0
  212. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/session_state/__init__.py +0 -0
  213. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/session_state/file_store.py +0 -0
  214. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/telemetry/__init__.py +0 -0
  215. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/telemetry/operation_summary.py +0 -0
  216. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/telemetry/session_selection.py +0 -0
  217. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/telemetry/sync_summary.py +0 -0
  218. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/validation/__init__.py +0 -0
  219. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/validation/integrity_validation.py +0 -0
  220. {shellbrain-0.1.25 → shellbrain-0.1.26}/app/periphery/validation/semantic_validation.py +0 -0
  221. {shellbrain-0.1.25 → shellbrain-0.1.26}/setup.cfg +0 -0
  222. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/dependency_links.txt +0 -0
  223. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/entry_points.txt +0 -0
  224. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/requires.txt +0 -0
  225. {shellbrain-0.1.25 → shellbrain-0.1.26}/shellbrain.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellbrain
3
- Version: 0.1.25
3
+ Version: 0.1.26
4
4
  Summary: Repo-scoped Shellbrain CLI with explicit evidence-backed writes.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,101 @@
1
+ """Add explicit problem-run token metrics."""
2
+
3
+ from alembic import op
4
+
5
+ from app.periphery.db.models.views import (
6
+ USAGE_PROBLEM_READ_ROI_LEGACY_SQL,
7
+ USAGE_PROBLEM_RUN_TOKENS_SQL,
8
+ USAGE_PROBLEM_TOKENS_LEGACY_SQL,
9
+ USAGE_READ_BEFORE_SOLVE_ROI_LEGACY_SQL,
10
+ )
11
+
12
+ revision = "20260422_0015"
13
+ down_revision = "20260422_0014"
14
+ branch_labels = None
15
+ depends_on = None
16
+
17
+
18
+ def _unsuffixed_proxy_sql(sql: str) -> str:
19
+ """Return the pre-legacy view name for downgrade compatibility."""
20
+
21
+ return (
22
+ sql.replace("usage_read_before_solve_roi_legacy", "usage_read_before_solve_roi")
23
+ .replace("usage_problem_read_roi_legacy", "usage_problem_read_roi")
24
+ .replace("usage_problem_tokens_legacy", "usage_problem_tokens")
25
+ )
26
+
27
+
28
+ def upgrade() -> None:
29
+ """Create problem-run windows and rename memory-derived token proxies."""
30
+
31
+ op.execute(
32
+ """
33
+ DROP VIEW IF EXISTS usage_read_before_solve_roi;
34
+ DROP VIEW IF EXISTS usage_problem_read_roi;
35
+ DROP VIEW IF EXISTS usage_problem_tokens;
36
+
37
+ CREATE TABLE problem_runs (
38
+ id TEXT PRIMARY KEY,
39
+ repo_id TEXT NOT NULL,
40
+ thread_id TEXT,
41
+ host_app TEXT,
42
+ host_session_key TEXT,
43
+ episode_id TEXT REFERENCES episodes(id) ON DELETE SET NULL,
44
+ status TEXT NOT NULL,
45
+ opened_at TIMESTAMPTZ NOT NULL,
46
+ closed_at TIMESTAMPTZ,
47
+ opened_by TEXT NOT NULL,
48
+ closed_by TEXT,
49
+ problem_memory_id TEXT REFERENCES memories(id) ON DELETE SET NULL,
50
+ solution_memory_id TEXT REFERENCES memories(id) ON DELETE SET NULL,
51
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
52
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
53
+ CONSTRAINT ck_problem_runs_status CHECK (status IN ('open', 'closed', 'abandoned')),
54
+ CONSTRAINT ck_problem_runs_opened_by CHECK (opened_by IN ('worker', 'librarian', 'manual', 'system')),
55
+ CONSTRAINT ck_problem_runs_closed_by CHECK (closed_by IS NULL OR closed_by IN ('worker', 'librarian', 'manual', 'system')),
56
+ CONSTRAINT ck_problem_runs_closed_after_opened CHECK (closed_at IS NULL OR closed_at >= opened_at),
57
+ CONSTRAINT ck_problem_runs_status_closed_at CHECK (
58
+ (
59
+ status = 'open'
60
+ AND closed_at IS NULL
61
+ ) OR (
62
+ status IN ('closed', 'abandoned')
63
+ AND closed_at IS NOT NULL
64
+ )
65
+ )
66
+ );
67
+
68
+ CREATE INDEX idx_problem_runs_repo_thread_window
69
+ ON problem_runs(repo_id, thread_id, opened_at, closed_at);
70
+ CREATE INDEX idx_problem_runs_repo_host_session_window
71
+ ON problem_runs(repo_id, host_app, host_session_key, opened_at, closed_at);
72
+ CREATE INDEX idx_problem_runs_repo_status_opened_at
73
+ ON problem_runs(repo_id, status, opened_at);
74
+ CREATE INDEX idx_problem_runs_problem_memory
75
+ ON problem_runs(problem_memory_id);
76
+ CREATE INDEX idx_problem_runs_solution_memory
77
+ ON problem_runs(solution_memory_id);
78
+ """
79
+ )
80
+ op.execute(USAGE_PROBLEM_TOKENS_LEGACY_SQL)
81
+ op.execute(USAGE_PROBLEM_READ_ROI_LEGACY_SQL)
82
+ op.execute(USAGE_READ_BEFORE_SOLVE_ROI_LEGACY_SQL)
83
+ op.execute(USAGE_PROBLEM_RUN_TOKENS_SQL)
84
+
85
+
86
+ def downgrade() -> None:
87
+ """Drop problem-run metrics and restore the previous proxy view names."""
88
+
89
+ op.execute(
90
+ """
91
+ DROP VIEW IF EXISTS usage_problem_run_tokens;
92
+ DROP TABLE IF EXISTS problem_runs;
93
+
94
+ DROP VIEW IF EXISTS usage_read_before_solve_roi_legacy;
95
+ DROP VIEW IF EXISTS usage_problem_read_roi_legacy;
96
+ DROP VIEW IF EXISTS usage_problem_tokens_legacy;
97
+ """
98
+ )
99
+ op.execute(_unsuffixed_proxy_sql(USAGE_PROBLEM_TOKENS_LEGACY_SQL))
100
+ op.execute(_unsuffixed_proxy_sql(USAGE_PROBLEM_READ_ROI_LEGACY_SQL))
101
+ op.execute(_unsuffixed_proxy_sql(USAGE_READ_BEFORE_SOLVE_ROI_LEGACY_SQL))
@@ -60,7 +60,7 @@ _TOP_LEVEL_HELP = dedent(
60
60
  Examples:
61
61
  shellbrain init
62
62
  shellbrain upgrade
63
- shellbrain metrics --days 30
63
+ shellbrain metrics
64
64
  shellbrain read --json '{"query":"Have we seen this migration lock timeout before?","kinds":["problem","solution","failed_tactic"]}'
65
65
  shellbrain read --json '{"query":"What repo constraints or user preferences matter for this auth refactor?","kinds":["fact","preference","change"]}'
66
66
  shellbrain events --json '{"limit":10}'
@@ -260,12 +260,10 @@ _BACKFILL_TOKEN_USAGE_HELP = dedent(
260
260
 
261
261
  _METRICS_HELP = dedent(
262
262
  """\
263
- Generate one lightweight repo-scoped metrics snapshot, write local artifacts, and open a static dashboard.
263
+ Generate lightweight metrics snapshots, write local artifacts, and browse repos with arrow keys.
264
264
 
265
- Examples:
265
+ Example:
266
266
  shellbrain metrics
267
- shellbrain metrics --days 30
268
- shellbrain metrics --days 14 --no-open
269
267
  """
270
268
  )
271
269
 
@@ -386,23 +384,11 @@ def build_parser() -> argparse.ArgumentParser:
386
384
 
387
385
  metrics_parser = subparsers.add_parser(
388
386
  "metrics",
389
- help="Open a lightweight repo-scoped metrics dashboard.",
390
- description="Generate one local metrics snapshot and open a static Shellbrain dashboard.",
387
+ help="Browse Shellbrain metrics across repos.",
388
+ description="Generate local metrics snapshots and browse one repo at a time in the terminal.",
391
389
  epilog=_METRICS_HELP,
392
390
  formatter_class=_HelpFormatter,
393
391
  )
394
- _add_repo_context_arguments(metrics_parser, suppress_default=True)
395
- metrics_parser.add_argument(
396
- "--days",
397
- type=int,
398
- default=30,
399
- help="Number of trailing days to include in the snapshot. Defaults to 30.",
400
- )
401
- metrics_parser.add_argument(
402
- "--no-open",
403
- action="store_true",
404
- help="Generate artifacts without opening the dashboard in the browser.",
405
- )
406
392
 
407
393
  create_parser = subparsers.add_parser(
408
394
  "create",
@@ -877,7 +863,7 @@ def _run_admin_command(args: argparse.Namespace) -> int:
877
863
 
878
864
 
879
865
  def _run_metrics_command(args: argparse.Namespace) -> int:
880
- """Generate one repo-scoped metrics snapshot and local dashboard artifacts."""
866
+ """Generate metrics snapshots and artifacts for one or many repos."""
881
867
 
882
868
  try:
883
869
  from app.boot.admin_db import get_optional_admin_db_dsn
@@ -885,43 +871,41 @@ def _run_metrics_command(args: argparse.Namespace) -> int:
885
871
  from app.periphery.db.engine import get_engine
886
872
  from app.periphery.metrics.artifacts import write_metrics_artifacts
887
873
  from app.periphery.metrics.browser import open_metrics_dashboard
874
+ from app.periphery.metrics.pager import present_metrics_repo_pager
888
875
  from app.periphery.metrics.render_html import render_metrics_dashboard
889
- from app.periphery.metrics.service import build_metrics_snapshot
876
+ from app.periphery.metrics.service import build_metrics_snapshot, list_metrics_repo_ids
877
+
878
+ if bool(getattr(args, "repo_id", None) or getattr(args, "repo_root", None) or getattr(args, "no_sync", False)):
879
+ raise ValueError("`shellbrain metrics` does not accept options. Run `shellbrain metrics`.")
890
880
 
891
- repo_context = resolve_repo_context(
892
- repo_root_arg=getattr(args, "repo_root", None),
893
- repo_id_arg=getattr(args, "repo_id", None),
894
- )
895
881
  _warn_or_fail_on_unsafe_app_role()
896
- _ensure_repo_registration_for_operation(
897
- repo_context=repo_context,
898
- repo_id_override=getattr(args, "repo_id", None),
899
- )
900
882
  dsn = get_optional_db_dsn() or get_optional_admin_db_dsn()
901
883
  if not dsn:
902
884
  raise RuntimeError("Shellbrain database is not configured. Run `shellbrain init` first.")
903
- snapshot = build_metrics_snapshot(
904
- engine=get_engine(dsn),
905
- repo_id=repo_context.repo_id,
906
- days=int(args.days),
907
- )
908
- html = render_metrics_dashboard(snapshot)
909
- paths = write_metrics_artifacts(repo_id=repo_context.repo_id, snapshot=snapshot, html=html)
910
- opened_dashboard = False
911
- if not bool(getattr(args, "no_open", False)):
912
- opened_dashboard = bool(open_metrics_dashboard(paths["html_path"]))
913
- print(f"Generated Shellbrain metrics for {repo_context.repo_id}")
914
- print(f"Status: {snapshot['status']} ({snapshot['confidence']} confidence)")
915
- print(f"JSON: {paths['json_path']}")
916
- print(f"Markdown: {paths['md_path']}")
917
- print(f"Dashboard: {paths['html_path']}")
885
+ engine = get_engine(dsn)
886
+
887
+ target_repo_ids = list_metrics_repo_ids(engine=engine)
888
+ if not target_repo_ids:
889
+ print("No tracked repos found in metrics telemetry yet.")
890
+ return 0
891
+
892
+ entries: list[dict[str, Any]] = []
893
+ window_days = 30
894
+ for repo_id in target_repo_ids:
895
+ snapshot = build_metrics_snapshot(
896
+ engine=engine,
897
+ repo_id=repo_id,
898
+ days=window_days,
899
+ )
900
+ html = render_metrics_dashboard(snapshot)
901
+ paths = write_metrics_artifacts(repo_id=repo_id, snapshot=snapshot, html=html)
902
+ entries.append({"snapshot": snapshot, "paths": paths})
903
+
904
+ print(f"Generated Shellbrain metrics for {len(entries)} repos")
905
+ print(f"Window: last {window_days} days")
918
906
  print("Artifacts: updated in place")
919
- if bool(getattr(args, "no_open", False)):
920
- print("Browser: skipped")
921
- elif opened_dashboard:
922
- print("Browser: opened dashboard")
923
- else:
924
- print("Browser: could not open automatically")
907
+ print("Browser: press 'o' in the viewer to open the current dashboard")
908
+ present_metrics_repo_pager(entries=entries, open_dashboard=open_metrics_dashboard)
925
909
  return 0
926
910
  except (RuntimeError, ValueError) as exc:
927
911
  print(str(exc), file=sys.stderr)
@@ -0,0 +1,60 @@
1
+ """SQLAlchemy Core table for explicit problem-run telemetry windows."""
2
+
3
+ from sqlalchemy import CheckConstraint, Column, ForeignKey, Index, String, Table, text
4
+ from sqlalchemy.dialects.postgresql import TIMESTAMP
5
+
6
+ from app.periphery.db.models.metadata import metadata
7
+
8
+
9
+ _PROBLEM_RUN_STATUSES = "'open', 'closed', 'abandoned'"
10
+ _PROBLEM_RUN_ACTORS = "'worker', 'librarian', 'manual', 'system'"
11
+
12
+
13
+ problem_runs = Table(
14
+ "problem_runs",
15
+ metadata,
16
+ Column("id", String, primary_key=True),
17
+ Column("repo_id", String, nullable=False),
18
+ Column("thread_id", String),
19
+ Column("host_app", String),
20
+ Column("host_session_key", String),
21
+ Column("episode_id", String, ForeignKey("episodes.id", ondelete="SET NULL")),
22
+ Column("status", String, nullable=False),
23
+ Column("opened_at", TIMESTAMP(timezone=True), nullable=False),
24
+ Column("closed_at", TIMESTAMP(timezone=True)),
25
+ Column("opened_by", String, nullable=False),
26
+ Column("closed_by", String),
27
+ Column("problem_memory_id", String, ForeignKey("memories.id", ondelete="SET NULL")),
28
+ Column("solution_memory_id", String, ForeignKey("memories.id", ondelete="SET NULL")),
29
+ Column("created_at", TIMESTAMP(timezone=True), nullable=False, server_default=text("NOW()")),
30
+ Column("updated_at", TIMESTAMP(timezone=True), nullable=False, server_default=text("NOW()")),
31
+ CheckConstraint(f"status IN ({_PROBLEM_RUN_STATUSES})", name="ck_problem_runs_status"),
32
+ CheckConstraint(f"opened_by IN ({_PROBLEM_RUN_ACTORS})", name="ck_problem_runs_opened_by"),
33
+ CheckConstraint(f"closed_by IS NULL OR closed_by IN ({_PROBLEM_RUN_ACTORS})", name="ck_problem_runs_closed_by"),
34
+ CheckConstraint("closed_at IS NULL OR closed_at >= opened_at", name="ck_problem_runs_closed_after_opened"),
35
+ CheckConstraint(
36
+ """
37
+ (
38
+ status = 'open'
39
+ AND closed_at IS NULL
40
+ ) OR (
41
+ status IN ('closed', 'abandoned')
42
+ AND closed_at IS NOT NULL
43
+ )
44
+ """,
45
+ name="ck_problem_runs_status_closed_at",
46
+ ),
47
+ )
48
+
49
+ Index("idx_problem_runs_repo_thread_window", problem_runs.c.repo_id, problem_runs.c.thread_id, problem_runs.c.opened_at, problem_runs.c.closed_at)
50
+ Index(
51
+ "idx_problem_runs_repo_host_session_window",
52
+ problem_runs.c.repo_id,
53
+ problem_runs.c.host_app,
54
+ problem_runs.c.host_session_key,
55
+ problem_runs.c.opened_at,
56
+ problem_runs.c.closed_at,
57
+ )
58
+ Index("idx_problem_runs_repo_status_opened_at", problem_runs.c.repo_id, problem_runs.c.status, problem_runs.c.opened_at)
59
+ Index("idx_problem_runs_problem_memory", problem_runs.c.problem_memory_id)
60
+ Index("idx_problem_runs_solution_memory", problem_runs.c.solution_memory_id)
@@ -8,12 +8,13 @@ from app.periphery.db.models import (
8
8
  experiences,
9
9
  instance_metadata,
10
10
  memories,
11
+ problem_runs,
11
12
  telemetry,
12
13
  utility,
13
14
  )
14
15
  from app.periphery.db.models.metadata import metadata
15
16
 
16
17
 
17
- _ = (associations, concepts, episodes, evidence, experiences, instance_metadata, memories, telemetry, utility)
18
+ _ = (associations, concepts, episodes, evidence, experiences, instance_metadata, memories, problem_runs, telemetry, utility)
18
19
 
19
20
  target_metadata = metadata
@@ -616,6 +616,218 @@ GROUP BY repo_id, solve_window, read_cohort;
616
616
  """
617
617
 
618
618
 
619
+ USAGE_PROBLEM_TOKENS_LEGACY_SQL = USAGE_PROBLEM_TOKENS_SQL.replace(
620
+ "CREATE OR REPLACE VIEW usage_problem_tokens AS",
621
+ "CREATE OR REPLACE VIEW usage_problem_tokens_legacy AS",
622
+ )
623
+ USAGE_PROBLEM_READ_ROI_LEGACY_SQL = (
624
+ USAGE_PROBLEM_READ_ROI_SQL.replace(
625
+ "CREATE OR REPLACE VIEW usage_problem_read_roi AS",
626
+ "CREATE OR REPLACE VIEW usage_problem_read_roi_legacy AS",
627
+ )
628
+ .replace("FROM usage_problem_tokens upt", "FROM usage_problem_tokens_legacy upt")
629
+ )
630
+ USAGE_READ_BEFORE_SOLVE_ROI_LEGACY_SQL = (
631
+ USAGE_READ_BEFORE_SOLVE_ROI_SQL.replace(
632
+ "CREATE OR REPLACE VIEW usage_read_before_solve_roi AS",
633
+ "CREATE OR REPLACE VIEW usage_read_before_solve_roi_legacy AS",
634
+ )
635
+ .replace("FROM usage_problem_read_roi", "FROM usage_problem_read_roi_legacy")
636
+ )
637
+
638
+
639
+ USAGE_PROBLEM_RUN_TOKENS_SQL = """
640
+ CREATE OR REPLACE VIEW usage_problem_run_tokens AS
641
+ WITH session_quality AS (
642
+ SELECT
643
+ repo_id,
644
+ host_app,
645
+ host_session_key,
646
+ BOOL_OR(capture_quality = 'exact') AS has_exact_rows,
647
+ BOOL_OR(
648
+ capture_quality = 'exact'
649
+ AND (
650
+ COALESCE(input_tokens, 0) > 0
651
+ OR COALESCE(output_tokens, 0) > 0
652
+ OR COALESCE(cached_input_tokens_total, 0) > 0
653
+ OR COALESCE(reasoning_output_tokens, 0) > 0
654
+ )
655
+ ) AS has_nonzero_exact_rows,
656
+ BOOL_OR(capture_quality = 'estimated') AS has_estimated_rows
657
+ FROM model_usage
658
+ GROUP BY repo_id, host_app, host_session_key
659
+ ),
660
+ preferred_rows AS (
661
+ SELECT mu.*
662
+ FROM model_usage mu
663
+ JOIN session_quality sq
664
+ ON sq.repo_id = mu.repo_id
665
+ AND sq.host_app = mu.host_app
666
+ AND sq.host_session_key = mu.host_session_key
667
+ WHERE (
668
+ sq.has_nonzero_exact_rows
669
+ AND mu.capture_quality = 'exact'
670
+ ) OR (
671
+ NOT sq.has_nonzero_exact_rows
672
+ AND mu.capture_quality = 'estimated'
673
+ ) OR (
674
+ NOT sq.has_nonzero_exact_rows
675
+ AND NOT sq.has_estimated_rows
676
+ AND sq.has_exact_rows
677
+ AND mu.capture_quality = 'exact'
678
+ )
679
+ ),
680
+ eligible_runs AS (
681
+ SELECT *
682
+ FROM problem_runs
683
+ WHERE status IN ('closed', 'abandoned')
684
+ AND closed_at IS NOT NULL
685
+ ),
686
+ run_usage AS (
687
+ SELECT
688
+ prun.id AS problem_run_id,
689
+ COUNT(mu.id)::INTEGER AS usage_row_count,
690
+ COUNT(mu.id) FILTER (WHERE mu.capture_quality = 'exact')::INTEGER AS exact_usage_row_count,
691
+ COUNT(mu.id) FILTER (WHERE mu.capture_quality = 'estimated')::INTEGER AS estimated_usage_row_count,
692
+ COALESCE(SUM(mu.input_tokens), 0)::BIGINT AS input_tokens,
693
+ COALESCE(SUM(mu.output_tokens), 0)::BIGINT AS output_tokens,
694
+ COALESCE(SUM(mu.reasoning_output_tokens), 0)::BIGINT AS reasoning_output_tokens,
695
+ COALESCE(SUM(mu.cached_input_tokens_total), 0)::BIGINT AS cached_input_tokens_total,
696
+ COALESCE(SUM(mu.cache_read_input_tokens), 0)::BIGINT AS cache_read_input_tokens,
697
+ COALESCE(SUM(mu.cache_creation_input_tokens), 0)::BIGINT AS cache_creation_input_tokens,
698
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)::BIGINT AS foreground_worker_input_tokens,
699
+ COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)::BIGINT AS foreground_worker_output_tokens,
700
+ (
701
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)
702
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)
703
+ )::BIGINT AS foreground_worker_fresh_work_tokens,
704
+ (
705
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)
706
+ + COALESCE(SUM(mu.cached_input_tokens_total) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)
707
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role IN ('foreground', 'worker')), 0)
708
+ )::BIGINT AS foreground_worker_all_tokens_including_cache,
709
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)::BIGINT AS librarian_input_tokens,
710
+ COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)::BIGINT AS librarian_output_tokens,
711
+ (
712
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)
713
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)
714
+ )::BIGINT AS librarian_fresh_work_tokens,
715
+ (
716
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)
717
+ + COALESCE(SUM(mu.cached_input_tokens_total) FILTER (WHERE mu.agent_role = 'librarian'), 0)
718
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role = 'librarian'), 0)
719
+ )::BIGINT AS librarian_all_tokens_including_cache,
720
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)::BIGINT AS other_role_input_tokens,
721
+ COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)::BIGINT AS other_role_output_tokens,
722
+ (
723
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)
724
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)
725
+ )::BIGINT AS other_role_fresh_work_tokens,
726
+ (
727
+ COALESCE(SUM(mu.input_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)
728
+ + COALESCE(SUM(mu.cached_input_tokens_total) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)
729
+ + COALESCE(SUM(mu.output_tokens) FILTER (WHERE mu.agent_role NOT IN ('foreground', 'worker', 'librarian')), 0)
730
+ )::BIGINT AS other_role_all_tokens_including_cache
731
+ FROM eligible_runs prun
732
+ LEFT JOIN preferred_rows mu
733
+ ON mu.repo_id = prun.repo_id
734
+ AND mu.occurred_at >= prun.opened_at
735
+ AND mu.occurred_at <= prun.closed_at
736
+ AND (
737
+ (
738
+ prun.host_app IS NOT NULL
739
+ AND prun.host_session_key IS NOT NULL
740
+ AND mu.host_app = prun.host_app
741
+ AND mu.host_session_key = prun.host_session_key
742
+ )
743
+ OR (
744
+ (prun.host_app IS NULL OR prun.host_session_key IS NULL)
745
+ AND prun.thread_id IS NOT NULL
746
+ AND mu.thread_id = prun.thread_id
747
+ )
748
+ )
749
+ GROUP BY prun.id
750
+ ),
751
+ run_reads AS (
752
+ SELECT
753
+ prun.id AS problem_run_id,
754
+ COUNT(oi.id)::INTEGER AS shellbrain_read_count,
755
+ COALESCE(SUM(ris.pack_token_estimate), 0)::BIGINT AS shellbrain_pack_tokens,
756
+ COALESCE(SUM(ris.concept_token_estimate), 0)::BIGINT AS shellbrain_concept_tokens
757
+ FROM eligible_runs prun
758
+ LEFT JOIN operation_invocations oi
759
+ ON oi.repo_id = prun.repo_id
760
+ AND oi.command = 'read'
761
+ AND oi.outcome = 'ok'
762
+ AND oi.created_at >= prun.opened_at
763
+ AND oi.created_at <= prun.closed_at
764
+ AND (
765
+ (
766
+ prun.host_app IS NOT NULL
767
+ AND prun.host_session_key IS NOT NULL
768
+ AND oi.selected_host_app = prun.host_app
769
+ AND oi.selected_host_session_key = prun.host_session_key
770
+ )
771
+ OR (
772
+ (prun.host_app IS NULL OR prun.host_session_key IS NULL)
773
+ AND prun.thread_id IS NOT NULL
774
+ AND oi.selected_thread_id = prun.thread_id
775
+ )
776
+ )
777
+ LEFT JOIN read_invocation_summaries ris ON ris.invocation_id = oi.id
778
+ GROUP BY prun.id
779
+ )
780
+ SELECT
781
+ prun.id AS problem_run_id,
782
+ prun.repo_id,
783
+ prun.thread_id,
784
+ prun.host_app,
785
+ prun.host_session_key,
786
+ prun.status,
787
+ prun.opened_at,
788
+ prun.closed_at,
789
+ EXTRACT(EPOCH FROM (prun.closed_at - prun.opened_at))::DOUBLE PRECISION AS duration_seconds,
790
+ prun.problem_memory_id,
791
+ prun.solution_memory_id,
792
+ ru.usage_row_count,
793
+ ru.exact_usage_row_count,
794
+ ru.estimated_usage_row_count,
795
+ ru.input_tokens,
796
+ ru.output_tokens,
797
+ ru.reasoning_output_tokens,
798
+ ru.cached_input_tokens_total,
799
+ ru.cache_read_input_tokens,
800
+ ru.cache_creation_input_tokens,
801
+ (
802
+ ru.input_tokens
803
+ + ru.output_tokens
804
+ )::BIGINT AS fresh_work_tokens,
805
+ (
806
+ ru.input_tokens
807
+ + ru.cached_input_tokens_total
808
+ + ru.output_tokens
809
+ )::BIGINT AS all_tokens_including_cache,
810
+ ru.foreground_worker_input_tokens,
811
+ ru.foreground_worker_output_tokens,
812
+ ru.foreground_worker_fresh_work_tokens,
813
+ ru.foreground_worker_all_tokens_including_cache,
814
+ ru.librarian_input_tokens,
815
+ ru.librarian_output_tokens,
816
+ ru.librarian_fresh_work_tokens,
817
+ ru.librarian_all_tokens_including_cache,
818
+ ru.other_role_input_tokens,
819
+ ru.other_role_output_tokens,
820
+ ru.other_role_fresh_work_tokens,
821
+ ru.other_role_all_tokens_including_cache,
822
+ rr.shellbrain_read_count,
823
+ rr.shellbrain_pack_tokens,
824
+ rr.shellbrain_concept_tokens
825
+ FROM eligible_runs prun
826
+ JOIN run_usage ru ON ru.problem_run_id = prun.id
827
+ JOIN run_reads rr ON rr.problem_run_id = prun.id;
828
+ """
829
+
830
+
619
831
  USAGE_TOKEN_CAPTURE_HEALTH_SQL = """
620
832
  CREATE OR REPLACE VIEW usage_token_capture_health AS
621
833
  WITH synced_sessions AS (