code-context-control 2.46.1__tar.gz → 2.49.1__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 (310) hide show
  1. {code_context_control-2.46.1/code_context_control.egg-info → code_context_control-2.49.1}/PKG-INFO +1 -1
  2. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/c3.py +11 -3
  3. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/commands/parser.py +10 -1
  4. {code_context_control-2.46.1 → code_context_control-2.49.1/code_context_control.egg-info}/PKG-INFO +1 -1
  5. {code_context_control-2.46.1 → code_context_control-2.49.1}/code_context_control.egg-info/SOURCES.txt +29 -1
  6. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/config.py +12 -0
  7. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/oracle_server.py +125 -20
  8. code_context_control-2.49.1/oracle/oracle_ui.html +1217 -0
  9. code_context_control-2.49.1/oracle/services/c3_bridge.py +372 -0
  10. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/chat_engine.py +453 -141
  11. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/federated_graph.py +52 -2
  12. code_context_control-2.49.1/oracle/services/local_session.py +54 -0
  13. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/ollama_bridge.py +87 -7
  14. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/project_scanner.py +60 -6
  15. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/review_agent.py +84 -1
  16. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/tool_registry.py +82 -3
  17. code_context_control-2.49.1/oracle/ui/activity.js +101 -0
  18. code_context_control-2.49.1/oracle/ui/agents.js +108 -0
  19. code_context_control-2.49.1/oracle/ui/app.js +11 -0
  20. code_context_control-2.49.1/oracle/ui/busy.js +102 -0
  21. code_context_control-2.49.1/oracle/ui/chat/conversations.js +153 -0
  22. code_context_control-2.49.1/oracle/ui/chat/input.js +284 -0
  23. code_context_control-2.49.1/oracle/ui/chat/markdown.js +33 -0
  24. code_context_control-2.49.1/oracle/ui/chat/send.js +281 -0
  25. code_context_control-2.49.1/oracle/ui/chat/stream_renderer.js +767 -0
  26. code_context_control-2.49.1/oracle/ui/chat/toolbar.js +270 -0
  27. code_context_control-2.49.1/oracle/ui/core.js +60 -0
  28. code_context_control-2.49.1/oracle/ui/crossgraph.js +184 -0
  29. code_context_control-2.49.1/oracle/ui/header.js +75 -0
  30. code_context_control-2.49.1/oracle/ui/insights.js +52 -0
  31. code_context_control-2.49.1/oracle/ui/projects.js +245 -0
  32. code_context_control-2.49.1/oracle/ui/settings.js +186 -0
  33. code_context_control-2.49.1/oracle/ui/suggestions.js +47 -0
  34. code_context_control-2.49.1/oracle/ui/theme_tabs.js +68 -0
  35. {code_context_control-2.46.1 → code_context_control-2.49.1}/pyproject.toml +5 -1
  36. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/project_runtime.py +16 -3
  37. code_context_control-2.49.1/tests/test_c3_bridge_project_artifacts.py +153 -0
  38. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_federated_graph.py +83 -0
  39. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_oracle_apikey_api.py +8 -2
  40. code_context_control-2.49.1/tests/test_oracle_c3_bridge.py +174 -0
  41. code_context_control-2.49.1/tests/test_oracle_chat_engine.py +551 -0
  42. code_context_control-2.49.1/tests/test_oracle_local_auth.py +228 -0
  43. code_context_control-2.49.1/tests/test_oracle_ollama_bridge.py +203 -0
  44. code_context_control-2.49.1/tests/test_oracle_scanner_cache.py +94 -0
  45. code_context_control-2.49.1/tests/test_oracle_subproject_awareness.py +131 -0
  46. code_context_control-2.49.1/tests/test_oracle_ui_bundle.py +86 -0
  47. code_context_control-2.49.1/tests/test_review_digest.py +184 -0
  48. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_tool_registry.py +32 -0
  49. code_context_control-2.46.1/oracle/oracle.html +0 -4136
  50. code_context_control-2.46.1/oracle/services/c3_bridge.py +0 -237
  51. {code_context_control-2.46.1 → code_context_control-2.49.1}/LICENSE +0 -0
  52. {code_context_control-2.46.1 → code_context_control-2.49.1}/README.md +0 -0
  53. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/__init__.py +0 -0
  54. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/_hook_utils.py +0 -0
  55. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/commands/__init__.py +0 -0
  56. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/commands/common.py +0 -0
  57. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/docs.html +0 -0
  58. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/edits.html +0 -0
  59. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/bitbucket.html +0 -0
  60. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/getting-started.html +0 -0
  61. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/index.html +0 -0
  62. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/oracle.html +0 -0
  63. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/shared.css +0 -0
  64. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/tools.html +0 -0
  65. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/guide/workflow.html +0 -0
  66. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_artifact.py +0 -0
  67. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_auto_snapshot.py +0 -0
  68. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_c3_signal.py +0 -0
  69. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_c3read.py +0 -0
  70. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_dispatch.py +0 -0
  71. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_edit_ledger.py +0 -0
  72. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_edit_unlock.py +0 -0
  73. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_filter.py +0 -0
  74. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_ghost_files.py +0 -0
  75. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_pretool_enforce.py +0 -0
  76. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_read.py +0 -0
  77. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_session_stats.py +0 -0
  78. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hook_terse_advisor.py +0 -0
  79. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub.html +0 -0
  80. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_server.py +0 -0
  81. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/app.js +0 -0
  82. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/add_project.js +0 -0
  83. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/config_editor.js +0 -0
  84. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
  85. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/drill_health.js +0 -0
  86. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/drill_panel.js +0 -0
  87. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/drill_tasks.js +0 -0
  88. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/drill_views.js +0 -0
  89. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/global_search.js +0 -0
  90. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/mcp_manager.js +0 -0
  91. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/modals.js +0 -0
  92. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/project_card.js +0 -0
  93. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/project_tree.js +0 -0
  94. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/session_drawer.js +0 -0
  95. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/settings_modal.js +0 -0
  96. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/sidebar.js +0 -0
  97. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/summary_bar.js +0 -0
  98. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/task_board.js +0 -0
  99. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/toasts.js +0 -0
  100. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/components/topbar.js +0 -0
  101. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui/state.js +0 -0
  102. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/hub_ui.html +0 -0
  103. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/mcp_proxy.py +0 -0
  104. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/mcp_server.py +0 -0
  105. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/server.py +0 -0
  106. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/__init__.py +0 -0
  107. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/_helpers.py +0 -0
  108. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/agent.py +0 -0
  109. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/artifacts.py +0 -0
  110. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/bitbucket.py +0 -0
  111. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/compress.py +0 -0
  112. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/delegate.py +0 -0
  113. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/edit.py +0 -0
  114. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/edits.py +0 -0
  115. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/federate.py +0 -0
  116. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/filter.py +0 -0
  117. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/impact.py +0 -0
  118. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/memory.py +0 -0
  119. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/project.py +0 -0
  120. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/read.py +0 -0
  121. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/search.py +0 -0
  122. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/session.py +0 -0
  123. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/shell.py +0 -0
  124. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/status.py +0 -0
  125. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/tasks.py +0 -0
  126. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/tools/validate.py +0 -0
  127. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/api.js +0 -0
  128. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/app.js +0 -0
  129. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/bitbucket.js +0 -0
  130. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/chat.js +0 -0
  131. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/dashboard.js +0 -0
  132. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/edits.js +0 -0
  133. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/instructions.js +0 -0
  134. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/memory.js +0 -0
  135. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/sessions.js +0 -0
  136. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/settings.js +0 -0
  137. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/sidebar.js +0 -0
  138. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/components/tasks.js +0 -0
  139. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/icons.js +0 -0
  140. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/pm_shared.js +0 -0
  141. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/shared.js +0 -0
  142. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui/theme.js +0 -0
  143. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui.html +0 -0
  144. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui_legacy.html +0 -0
  145. {code_context_control-2.46.1 → code_context_control-2.49.1}/cli/ui_nano.html +0 -0
  146. {code_context_control-2.46.1 → code_context_control-2.49.1}/code_context_control.egg-info/dependency_links.txt +0 -0
  147. {code_context_control-2.46.1 → code_context_control-2.49.1}/code_context_control.egg-info/entry_points.txt +0 -0
  148. {code_context_control-2.46.1 → code_context_control-2.49.1}/code_context_control.egg-info/requires.txt +0 -0
  149. {code_context_control-2.46.1 → code_context_control-2.49.1}/code_context_control.egg-info/top_level.txt +0 -0
  150. {code_context_control-2.46.1 → code_context_control-2.49.1}/core/__init__.py +0 -0
  151. {code_context_control-2.46.1 → code_context_control-2.49.1}/core/config.py +0 -0
  152. {code_context_control-2.46.1 → code_context_control-2.49.1}/core/ide.py +0 -0
  153. {code_context_control-2.46.1 → code_context_control-2.49.1}/core/mcp_toml.py +0 -0
  154. {code_context_control-2.46.1 → code_context_control-2.49.1}/core/web_security.py +0 -0
  155. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/__init__.py +0 -0
  156. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/mcp_oracle.py +0 -0
  157. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/__init__.py +0 -0
  158. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/activity_reporter.py +0 -0
  159. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/api_auth.py +0 -0
  160. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/chat_store.py +0 -0
  161. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/cross_memory.py +0 -0
  162. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/health_checker.py +0 -0
  163. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/insight_engine.py +0 -0
  164. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/memory_reader.py +0 -0
  165. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/memory_writer.py +0 -0
  166. {code_context_control-2.46.1 → code_context_control-2.49.1}/oracle/services/tool_executor.py +0 -0
  167. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/__init__.py +0 -0
  168. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/activity_log.py +0 -0
  169. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/agent_base.py +0 -0
  170. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/agents.py +0 -0
  171. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/artifact_defs.py +0 -0
  172. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/artifact_store.py +0 -0
  173. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/auto_memory.py +0 -0
  174. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bench/__init__.py +0 -0
  175. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bench/external/__init__.py +0 -0
  176. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bench/external/aider_polyglot.py +0 -0
  177. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bench/external/swe_bench.py +0 -0
  178. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/benchmark_dashboard.py +0 -0
  179. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bitbucket_client.py +0 -0
  180. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/bitbucket_credentials.py +0 -0
  181. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/circuit_breaker.py +0 -0
  182. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/claude_md.py +0 -0
  183. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/compressor.py +0 -0
  184. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/context_snapshot.py +0 -0
  185. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/conversation_store.py +0 -0
  186. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/doc_index.py +0 -0
  187. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/e2e_benchmark.py +0 -0
  188. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/e2e_evaluator.py +0 -0
  189. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/e2e_tasks.py +0 -0
  190. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/edit_ledger.py +0 -0
  191. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/embedding_index.py +0 -0
  192. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/error_reporting.py +0 -0
  193. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/file_memory.py +0 -0
  194. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/git_context.py +0 -0
  195. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/hub_service.py +0 -0
  196. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/indexer.py +0 -0
  197. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/memory.py +0 -0
  198. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/memory_consolidator.py +0 -0
  199. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/memory_graph.py +0 -0
  200. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/memory_grounder.py +0 -0
  201. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/memory_scorer.py +0 -0
  202. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/metrics.py +0 -0
  203. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/notifications.py +0 -0
  204. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/ollama_client.py +0 -0
  205. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/output_filter.py +0 -0
  206. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/parser.py +0 -0
  207. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/project_manager.py +0 -0
  208. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/protocol.py +0 -0
  209. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/proxy_state.py +0 -0
  210. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/retention.py +0 -0
  211. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/retrieval_broker.py +0 -0
  212. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/router.py +0 -0
  213. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/runtime.py +0 -0
  214. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/session_benchmark.py +0 -0
  215. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/session_manager.py +0 -0
  216. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/session_preloader.py +0 -0
  217. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/subprojects.py +0 -0
  218. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/task_store.py +0 -0
  219. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/telemetry.py +0 -0
  220. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/text_index.py +0 -0
  221. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/tool_classifier.py +0 -0
  222. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/transcript_index.py +0 -0
  223. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/validation_cache.py +0 -0
  224. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/vector_store.py +0 -0
  225. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/watcher.py +0 -0
  226. {code_context_control-2.46.1 → code_context_control-2.49.1}/services/win_subprocess.py +0 -0
  227. {code_context_control-2.46.1 → code_context_control-2.49.1}/setup.cfg +0 -0
  228. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_activity_reporter.py +0 -0
  229. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_aider_polyglot.py +0 -0
  230. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_artifact_store.py +0 -0
  231. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_artifact_tool.py +0 -0
  232. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_bitbucket_cli_smoke.py +0 -0
  233. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_bitbucket_client.py +0 -0
  234. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_bitbucket_config_fallback.py +0 -0
  235. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_bitbucket_credentials.py +0 -0
  236. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_bitbucket_tool.py +0 -0
  237. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_c3_shell.py +0 -0
  238. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_circuit_breaker.py +0 -0
  239. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_claude_md_merge.py +0 -0
  240. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_cli_smoke.py +0 -0
  241. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_compressor_large_file.py +0 -0
  242. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_delegate_cascade.py +0 -0
  243. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_e2e_benchmark.py +0 -0
  244. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_edit_ledger_hook.py +0 -0
  245. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_edit_normalization.py +0 -0
  246. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_enforcement_flip.py +0 -0
  247. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_filter_backoff.py +0 -0
  248. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_ghost_files.py +0 -0
  249. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_ghost_generation.py +0 -0
  250. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_git_branch_awareness.py +0 -0
  251. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hook_dispatch.py +0 -0
  252. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hook_pretool_enforce.py +0 -0
  253. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hook_smoke.py +0 -0
  254. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hook_state.py +0 -0
  255. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hub_inspect_api.py +0 -0
  256. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_hub_server_smoke.py +0 -0
  257. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_install_mcp_entrypoint.py +0 -0
  258. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_lazy_store_init.py +0 -0
  259. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_mcp_host_guard.py +0 -0
  260. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_mcp_server_smoke.py +0 -0
  261. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_mcp_toml.py +0 -0
  262. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_memory_graph_api.py +0 -0
  263. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_memory_system.py +0 -0
  264. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_notification_dedup.py +0 -0
  265. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_notification_discipline.py +0 -0
  266. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_oracle_api_auth.py +0 -0
  267. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_oracle_discovery_api.py +0 -0
  268. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_oracle_security_fixes.py +0 -0
  269. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_output_filter.py +0 -0
  270. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_permissions.py +0 -0
  271. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_pm_api.py +0 -0
  272. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_project_manager.py +0 -0
  273. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_project_manager_merge.py +0 -0
  274. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_project_tool.py +0 -0
  275. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_read_coercion.py +0 -0
  276. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_response_boilerplate.py +0 -0
  277. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_retention.py +0 -0
  278. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_service_durability.py +0 -0
  279. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_session_benchmark.py +0 -0
  280. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_session_budget.py +0 -0
  281. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_shell_robustness.py +0 -0
  282. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_subproject_exclusion.py +0 -0
  283. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_subproject_federation.py +0 -0
  284. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_subprojects.py +0 -0
  285. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_swe_bench.py +0 -0
  286. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_task_store.py +0 -0
  287. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_task_tool.py +0 -0
  288. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_token_telemetry.py +0 -0
  289. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_upgrade_and_version.py +0 -0
  290. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_validate.py +0 -0
  291. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_web_security.py +0 -0
  292. {code_context_control-2.46.1 → code_context_control-2.49.1}/tests/test_windows_reliability.py +0 -0
  293. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/__init__.py +0 -0
  294. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/backend.py +0 -0
  295. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/main.py +0 -0
  296. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/__init__.py +0 -0
  297. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/benchmark_view.py +0 -0
  298. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/claudemd_view.py +0 -0
  299. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/compress_view.py +0 -0
  300. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/index_view.py +0 -0
  301. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/init_view.py +0 -0
  302. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/mcp_view.py +0 -0
  303. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/optimize_view.py +0 -0
  304. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/pipe_view.py +0 -0
  305. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/projects_view.py +0 -0
  306. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/search_view.py +0 -0
  307. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/session_view.py +0 -0
  308. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/stats.py +0 -0
  309. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/screens/ui_view.py +0 -0
  310. {code_context_control-2.46.1 → code_context_control-2.49.1}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.46.1
3
+ Version: 2.49.1
4
4
  Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Gemini, Copilot). Retrieve less, read less, edit safer — and version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP).
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -85,7 +85,7 @@ console = Console() if HAS_RICH else None
85
85
  # Config
86
86
  CONFIG_DIR = ".c3"
87
87
  CONFIG_FILE = ".c3/config.json"
88
- __version__ = "2.46.1"
88
+ __version__ = "2.49.1"
89
89
 
90
90
 
91
91
  def _command_deps() -> CommandDeps:
@@ -5632,10 +5632,18 @@ def _bb_cmd_set_default(args, project_path: str) -> None:
5632
5632
 
5633
5633
 
5634
5634
  def cmd_oracle(args):
5635
- """Oracle Discovery API key + connection management."""
5635
+ """Oracle dashboard server + Discovery API key management."""
5636
5636
  sub = getattr(args, "oracle_cmd", None)
5637
+ if sub in ("serve", "start"):
5638
+ # Lazy import: run_oracle builds all Oracle services (matches
5639
+ # cmd_hub's deferred-import style so bare `c3` stays fast).
5640
+ from oracle.oracle_server import run_oracle
5641
+ run_oracle(port=getattr(args, "port", None),
5642
+ open_browser=not getattr(args, "no_browser", False))
5643
+ return
5637
5644
  if sub != "api":
5638
- print("Usage: c3 oracle api {info,key,rotate,clear}")
5645
+ print("Usage: c3 oracle {serve,api} — serve: launch the dashboard; "
5646
+ "api {info,key,rotate,clear}: manage the Discovery key")
5639
5647
  return
5640
5648
 
5641
5649
  from oracle.config import load_config
@@ -357,9 +357,18 @@ def build_parser(version: str, parse_cli_ide_arg):
357
357
  # ── Oracle Discovery API (v2.32.0) ──────────────────────────────────
358
358
  p_oracle = subparsers.add_parser(
359
359
  "oracle",
360
- help="Oracle Discovery API key + connection management",
360
+ help="Oracle dashboard server + Discovery API key management",
361
361
  )
362
362
  or_subs = p_oracle.add_subparsers(dest="oracle_cmd")
363
+ or_serve = or_subs.add_parser(
364
+ "serve",
365
+ aliases=["start"],
366
+ help="Launch the Oracle dashboard server (REST + MCP discovery endpoints)",
367
+ )
368
+ or_serve.add_argument("--port", type=int, default=None,
369
+ help="Server port (default: config 'port', 3331)")
370
+ or_serve.add_argument("--no-browser", action="store_true",
371
+ help="Don't open the browser")
363
372
  or_api = or_subs.add_parser(
364
373
  "api",
365
374
  help="Show connection info / manage the Discovery API key",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.46.1
3
+ Version: 2.49.1
4
4
  Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Gemini, Copilot). Retrieve less, read less, edit safer — and version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP).
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -110,8 +110,8 @@ core/web_security.py
110
110
  oracle/__init__.py
111
111
  oracle/config.py
112
112
  oracle/mcp_oracle.py
113
- oracle/oracle.html
114
113
  oracle/oracle_server.py
114
+ oracle/oracle_ui.html
115
115
  oracle/services/__init__.py
116
116
  oracle/services/activity_reporter.py
117
117
  oracle/services/api_auth.py
@@ -122,6 +122,7 @@ oracle/services/cross_memory.py
122
122
  oracle/services/federated_graph.py
123
123
  oracle/services/health_checker.py
124
124
  oracle/services/insight_engine.py
125
+ oracle/services/local_session.py
125
126
  oracle/services/memory_reader.py
126
127
  oracle/services/memory_writer.py
127
128
  oracle/services/ollama_bridge.py
@@ -129,6 +130,24 @@ oracle/services/project_scanner.py
129
130
  oracle/services/review_agent.py
130
131
  oracle/services/tool_executor.py
131
132
  oracle/services/tool_registry.py
133
+ oracle/ui/activity.js
134
+ oracle/ui/agents.js
135
+ oracle/ui/app.js
136
+ oracle/ui/busy.js
137
+ oracle/ui/core.js
138
+ oracle/ui/crossgraph.js
139
+ oracle/ui/header.js
140
+ oracle/ui/insights.js
141
+ oracle/ui/projects.js
142
+ oracle/ui/settings.js
143
+ oracle/ui/suggestions.js
144
+ oracle/ui/theme_tabs.js
145
+ oracle/ui/chat/conversations.js
146
+ oracle/ui/chat/input.js
147
+ oracle/ui/chat/markdown.js
148
+ oracle/ui/chat/send.js
149
+ oracle/ui/chat/stream_renderer.js
150
+ oracle/ui/chat/toolbar.js
132
151
  services/__init__.py
133
152
  services/activity_log.py
134
153
  services/agent_base.py
@@ -199,6 +218,7 @@ tests/test_bitbucket_client.py
199
218
  tests/test_bitbucket_config_fallback.py
200
219
  tests/test_bitbucket_credentials.py
201
220
  tests/test_bitbucket_tool.py
221
+ tests/test_c3_bridge_project_artifacts.py
202
222
  tests/test_c3_shell.py
203
223
  tests/test_circuit_breaker.py
204
224
  tests/test_claude_md_merge.py
@@ -231,8 +251,15 @@ tests/test_notification_dedup.py
231
251
  tests/test_notification_discipline.py
232
252
  tests/test_oracle_api_auth.py
233
253
  tests/test_oracle_apikey_api.py
254
+ tests/test_oracle_c3_bridge.py
255
+ tests/test_oracle_chat_engine.py
234
256
  tests/test_oracle_discovery_api.py
257
+ tests/test_oracle_local_auth.py
258
+ tests/test_oracle_ollama_bridge.py
259
+ tests/test_oracle_scanner_cache.py
235
260
  tests/test_oracle_security_fixes.py
261
+ tests/test_oracle_subproject_awareness.py
262
+ tests/test_oracle_ui_bundle.py
236
263
  tests/test_output_filter.py
237
264
  tests/test_permissions.py
238
265
  tests/test_pm_api.py
@@ -242,6 +269,7 @@ tests/test_project_tool.py
242
269
  tests/test_read_coercion.py
243
270
  tests/test_response_boilerplate.py
244
271
  tests/test_retention.py
272
+ tests/test_review_digest.py
245
273
  tests/test_service_durability.py
246
274
  tests/test_session_benchmark.py
247
275
  tests/test_session_budget.py
@@ -18,12 +18,21 @@ DEFAULTS = {
18
18
  "mcp_port": 3332, # discovery MCP transport port (loopback)
19
19
  "ollama_base_url": "https://ollama.com",
20
20
  "ollama_api_key": "",
21
+ "llm_cache_ttl_sec": 86400, # disk cache TTL for generate() responses
21
22
  "model": "gemma4:31b-cloud",
22
23
  "hub_url": "http://localhost:3330",
24
+ "scanner_ttl_seconds": 20,
23
25
  "review_interval_seconds": 1800,
24
26
  "review_enabled": True,
27
+ # ── Scheduled activity digest (runs inside the review loop) ──
28
+ "digest_enabled": False, # off = current behavior (on-demand only)
29
+ "digest_interval_seconds": 86400, # daily cadence once enabled
30
+ "digest_narrate": False, # LLM prose costs a cloud call; opt-in
31
+ "digest_notify_file": "", # "" = disabled; else JSONL sink path
32
+ "digest_retention_days": 14, # prune stored digests
25
33
  "auto_open_browser": True,
26
34
  "theme": "dark",
35
+ "ui_last_tab": "chat", # persisted UI preference (hub parity)
27
36
  "max_facts_per_analysis": 100,
28
37
  "insight_confidence_threshold": 0.5,
29
38
  "log_level": "INFO",
@@ -39,6 +48,7 @@ DEFAULTS = {
39
48
  "description": "Expert in system architecture, design patterns, and cross-project structure. Best for high-level analysis.",
40
49
  "system_prompt": "You are the Architect. Focus on structural integrity, design patterns, and the big picture. Provide high-level recommendations before diving into code.",
41
50
  "model": "gemma4:31b-cloud",
51
+ "backend": "ollama",
42
52
  "active": True
43
53
  },
44
54
  {
@@ -47,6 +57,7 @@ DEFAULTS = {
47
57
  "description": "Specializes in deep code analysis, bug hunting, and tracing execution paths.",
48
58
  "system_prompt": "You are the Code Explorer. Be incredibly precise, cite specific lines of code, and focus on the technical implementation details. Trace logic thoroughly.",
49
59
  "model": "gemma4:31b-cloud",
60
+ "backend": "ollama",
50
61
  "active": True
51
62
  },
52
63
  {
@@ -55,6 +66,7 @@ DEFAULTS = {
55
66
  "description": "Focuses on analyzing project memory, facts, and insights.",
56
67
  "system_prompt": "You are the Memory Analyst. Rely heavily on memory tools and facts to spot trends. Connect current issues to past context.",
57
68
  "model": "gemma4:31b-cloud",
69
+ "backend": "ollama",
58
70
  "active": True
59
71
  }
60
72
  ],
@@ -3,7 +3,6 @@
3
3
  import atexit
4
4
  import json
5
5
  import logging
6
- import os
7
6
  import socket
8
7
  import sys
9
8
  import threading
@@ -16,11 +15,11 @@ _PROJECT_ROOT = Path(__file__).resolve().parent.parent
16
15
  if str(_PROJECT_ROOT) not in sys.path:
17
16
  sys.path.insert(0, str(_PROJECT_ROOT))
18
17
 
19
- from flask import Flask, Response, jsonify, request, send_from_directory # noqa: E402
18
+ from flask import Flask, Response, jsonify, request # noqa: E402
20
19
 
21
20
  from oracle.config import ORACLE_DIR, load_config, save_config # noqa: E402
22
21
  from oracle.mcp_oracle import mcp_url, start_mcp_thread # noqa: E402
23
- from oracle.services import api_auth # noqa: E402
22
+ from oracle.services import api_auth, local_session # noqa: E402
24
23
  from oracle.services.activity_reporter import ActivityReporter # noqa: E402
25
24
  from oracle.services.api_auth import extract_bearer # noqa: E402
26
25
  from oracle.services.api_auth import verify as verify_api_key # noqa: E402
@@ -68,6 +67,7 @@ def _init_services():
68
67
  base_url=_cfg.get("ollama_base_url", "https://ollama.com"),
69
68
  model=_cfg.get("model", "gemma4:31b-cloud"),
70
69
  api_key=_cfg.get("ollama_api_key", ""),
70
+ cache_ttl_sec=int(_cfg.get("llm_cache_ttl_sec", 86400)),
71
71
  )
72
72
  # Verify model works on startup (background thread to avoid blocking)
73
73
  def _verify():
@@ -80,7 +80,10 @@ def _init_services():
80
80
  _model_verified = False
81
81
  logging.getLogger("oracle").warning("Ollama unreachable — model not verified")
82
82
  threading.Thread(target=_verify, daemon=True, name="oracle-model-verify").start()
83
- _scanner = ProjectScanner(hub_url=_cfg.get("hub_url", "http://localhost:3330"))
83
+ _scanner = ProjectScanner(
84
+ hub_url=_cfg.get("hub_url", "http://localhost:3330"),
85
+ ttl=float(_cfg.get("scanner_ttl_seconds", 20)),
86
+ )
84
87
  _reader = MemoryReader()
85
88
  _checker = HealthChecker(_reader)
86
89
  _writer = MemoryWriter()
@@ -89,6 +92,8 @@ def _init_services():
89
92
  _chat_store = ChatStore()
90
93
  _c3_bridge = C3Bridge(scanner=_scanner)
91
94
  _federated = FederatedGraph(reader=_reader, cross_memory=_cross_memory, ollama_bridge=_bridge)
95
+ # Reporter before ReviewAgent: the review loop emits the scheduled digest.
96
+ _activity_reporter = ActivityReporter(scanner=_scanner, ollama_bridge=_bridge)
92
97
  _agent = ReviewAgent(
93
98
  scanner=_scanner,
94
99
  reader=_reader,
@@ -98,8 +103,8 @@ def _init_services():
98
103
  writer=_writer,
99
104
  interval=int(_cfg.get("review_interval_seconds", 1800)),
100
105
  federated_graph=_federated,
106
+ activity_reporter=_activity_reporter,
101
107
  )
102
- _activity_reporter = ActivityReporter(scanner=_scanner, ollama_bridge=_bridge)
103
108
  _chat_engine = ChatEngine(
104
109
  bridge=_bridge,
105
110
  reader=_reader,
@@ -122,9 +127,10 @@ def _init_services():
122
127
  # ── CORS ──────────────────────────────────────────────────
123
128
  # Localhost security guard + scoped CORS (replaces the previous wildcard CORS).
124
129
  # Host-header allowlist + Origin/Referer CSRF guard. Bearer auth on
125
- # /api/discovery/* (see _discovery_auth_guard below) still applies on top; this
126
- # guard also protects the ungated endpoints (config, chat, /api/apikey) from
127
- # cross-origin reads notably the raw Discovery token returned by api_apikey_get.
130
+ # /api/discovery/* (see _discovery_auth_guard below) and the local write gate
131
+ # (_local_write_guard: session cookie or Bearer on every other mutating
132
+ # /api/* call) still apply on top; this guard blocks cross-origin browsers,
133
+ # the write gate blocks unauthenticated local processes.
128
134
  from core.web_security import (
129
135
  allowed_hostnames as _allowed_hostnames,
130
136
  )
@@ -155,10 +161,90 @@ def _discovery_auth_guard():
155
161
  return None
156
162
 
157
163
 
164
+ # ── Local write gate ──────────────────────────────────────
165
+ @app.before_request
166
+ def _local_write_guard():
167
+ """Auth gate for mutating local API calls (everything except Discovery).
168
+
169
+ Requires either the per-boot dashboard session cookie (issued on ``GET /``
170
+ to loopback browsers) or the Discovery Bearer token. Default-deny: any
171
+ future mutating ``/api/*`` endpoint is covered automatically. Closes the
172
+ rotate-then-read kill chain — previously any local process could POST
173
+ /api/apikey/rotate unauthenticated and read the fresh token, defeating
174
+ the Bearer gates on /api/config and /api/discovery/*.
175
+ """
176
+ path = request.path or ""
177
+ if not path.startswith("/api/") or path.startswith("/api/discovery"):
178
+ return None
179
+ if request.method in ("GET", "HEAD", "OPTIONS"):
180
+ return None
181
+ if verify_api_key(extract_bearer(request.headers.get("Authorization"))):
182
+ return None
183
+ if local_session.verify(request.cookies.get(local_session.COOKIE_NAME)):
184
+ return None
185
+ return jsonify({"error": "unauthorized"}), 401
186
+
187
+
158
188
  # ── Static ────────────────────────────────────────────────
189
+
190
+ # JS load order for the concatenated Oracle UI build (mirrors cli/hub_server.py).
191
+ # One shared script scope: function declarations hoist across files, and
192
+ # app.js (the init IIFE) must stay LAST.
193
+ _ORACLE_JS_FILES = [
194
+ "ui/core.js",
195
+ "ui/busy.js",
196
+ "ui/theme_tabs.js",
197
+ "ui/crossgraph.js",
198
+ "ui/header.js",
199
+ "ui/projects.js",
200
+ "ui/insights.js",
201
+ "ui/activity.js",
202
+ "ui/suggestions.js",
203
+ "ui/settings.js",
204
+ "ui/agents.js",
205
+ "ui/chat/markdown.js",
206
+ "ui/chat/conversations.js",
207
+ "ui/chat/stream_renderer.js",
208
+ "ui/chat/toolbar.js",
209
+ "ui/chat/input.js",
210
+ "ui/chat/send.js",
211
+ "ui/app.js",
212
+ ]
213
+
214
+
215
+ def _build_oracle_html() -> str:
216
+ """Concatenate oracle_ui.html shell + all JS module files into one response."""
217
+ oracle_dir = Path(__file__).parent
218
+ shell_path = oracle_dir / "oracle_ui.html"
219
+ if not shell_path.exists():
220
+ return "<h1>Oracle UI not found.</h1>"
221
+
222
+ shell = shell_path.read_text(encoding="utf-8")
223
+
224
+ js_parts = []
225
+ for rel in _ORACLE_JS_FILES:
226
+ js_path = oracle_dir / rel
227
+ if js_path.exists():
228
+ js_parts.append(f"// ═══ {rel} ═══\n" + js_path.read_text(encoding="utf-8"))
229
+
230
+ return shell.replace("/* __C3_ORACLE_SCRIPTS__ */", "\n\n".join(js_parts))
231
+
232
+
233
+ # Cache the built HTML (built on first request; cleared on server restart).
234
+ _oracle_html_cache: str | None = None
235
+
236
+
159
237
  @app.route("/")
160
238
  def index():
161
- return send_from_directory(os.path.dirname(__file__), "oracle.html")
239
+ global _oracle_html_cache
240
+ if _oracle_html_cache is None:
241
+ _oracle_html_cache = _build_oracle_html()
242
+ resp = Response(_oracle_html_cache, mimetype="text/html")
243
+ # Issue the dashboard session cookie only to loopback browsers; remote
244
+ # viewers (LAN bind) can read GET dashboards but cannot mutate.
245
+ if local_session.is_loopback(request.remote_addr):
246
+ local_session.attach_cookie(resp)
247
+ return resp
162
248
 
163
249
 
164
250
  # ── Health ────────────────────────────────────────────────
@@ -176,6 +262,7 @@ def api_health():
176
262
  return jsonify({
177
263
  "status": "ok",
178
264
  "service": "c3-oracle",
265
+ "version": _c3_version(),
179
266
  "model": _cfg.get("model", "gemma4:31b-cloud"),
180
267
  "ollama_available": ollama_ok,
181
268
  "model_verified": _model_verified,
@@ -206,11 +293,9 @@ _CONFIG_SETTABLE_KEYS = frozenset(_CONFIG_DEFAULTS.keys())
206
293
  @app.route("/api/config", methods=["POST"])
207
294
  def api_config_set():
208
295
  global _cfg
209
- # Require the Bearer token: this endpoint can disable Discovery auth or
210
- # repoint ollama_base_url, so it must never be reachable unauthenticated.
211
- token = extract_bearer(request.headers.get("Authorization"))
212
- if not verify_api_key(token):
213
- return jsonify({"error": "unauthorized"}), 401
296
+ # Auth is enforced by _local_write_guard (session cookie or Bearer): this
297
+ # endpoint can disable Discovery auth or repoint ollama_base_url, so it
298
+ # must never be reachable unauthenticated.
214
299
  body = request.get_json(silent=True) or {}
215
300
  if not isinstance(body, dict):
216
301
  return jsonify({"error": "body must be a JSON object"}), 400
@@ -258,7 +343,8 @@ def api_projects():
258
343
 
259
344
  @app.route("/api/projects/scan", methods=["POST"])
260
345
  def api_projects_scan():
261
- projects = _scanner.discover() if _scanner else []
346
+ # Explicit Scan action bypasses the scanner's TTL cache.
347
+ projects = _scanner.discover(force=True) if _scanner else []
262
348
  return jsonify({"scanned": len(projects), "projects": projects})
263
349
 
264
350
 
@@ -635,6 +721,23 @@ def api_chat_conversation_state(conv_id):
635
721
 
636
722
 
637
723
  # ── Activity digest (Oracle UI) ───────────────────────────
724
+ @app.route("/api/activity/digest/latest", methods=["GET"])
725
+ def api_activity_digest_latest():
726
+ """Most recent SCHEDULED digest (written by the review loop), or null.
727
+
728
+ On-demand digests via /api/activity/digest are not persisted here; this
729
+ serves ~/.c3/oracle/activity_digests/latest.json.
730
+ """
731
+ latest = ORACLE_DIR / "activity_digests" / "latest.json"
732
+ try:
733
+ if latest.is_file():
734
+ return Response(latest.read_text(encoding="utf-8"),
735
+ mimetype="application/json")
736
+ except Exception as e:
737
+ return jsonify({"error": str(e)}), 500
738
+ return jsonify({"digest": None, "generated_at": None})
739
+
740
+
638
741
  @app.route("/api/activity/digest", methods=["GET"])
639
742
  def api_activity_digest():
640
743
  """Cross-project activity digest for the Oracle UI.
@@ -740,7 +843,7 @@ def api_discovery_mcp_info():
740
843
  })
741
844
 
742
845
 
743
- # ── Discovery API key management (local dashboard unauthenticated, loopback) ──
846
+ # ── Discovery API key management (mutations gated by _local_write_guard) ──
744
847
  def _apikey_status(reveal: bool = False) -> dict:
745
848
  """Status payload for the Discovery API token + connection info.
746
849
 
@@ -786,12 +889,14 @@ def _apikey_status(reveal: bool = False) -> dict:
786
889
  def api_apikey_get():
787
890
  """Return Discovery API token status + connection info.
788
891
 
789
- The unmasked token is only included when the caller presents a valid Bearer
790
- token; otherwise only the masked form is returned (never the raw key over
791
- HTTP unauthenticated).
892
+ The unmasked token is only included when the caller presents a valid
893
+ Bearer token or the dashboard session cookie; otherwise only the masked
894
+ form is returned (never the raw key over HTTP unauthenticated).
792
895
  """
793
896
  try:
794
- reveal = verify_api_key(extract_bearer(request.headers.get("Authorization")))
897
+ reveal = verify_api_key(
898
+ extract_bearer(request.headers.get("Authorization"))
899
+ ) or local_session.verify(request.cookies.get(local_session.COOKIE_NAME))
795
900
  return jsonify(_apikey_status(reveal=reveal))
796
901
  except Exception as e:
797
902
  return jsonify({"error": str(e)}), 500