code-context-control 2.124.0__tar.gz → 2.125.0__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 (517) hide show
  1. {code_context_control-2.124.0/code_context_control.egg-info → code_context_control-2.125.0}/PKG-INFO +2 -2
  2. {code_context_control-2.124.0 → code_context_control-2.125.0}/README.md +1 -1
  3. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/c3.py +1 -1
  4. {code_context_control-2.124.0 → code_context_control-2.125.0/code_context_control.egg-info}/PKG-INFO +2 -2
  5. {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/SOURCES.txt +4 -0
  6. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/config.py +5 -0
  7. code_context_control-2.125.0/oracle/listeners.py +210 -0
  8. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/oracle_server.py +88 -2
  9. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/oracle_ui.html +9 -3
  10. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_poll.py +8 -3
  11. code_context_control-2.125.0/oracle/services/client_tokens.py +239 -0
  12. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/mobile_api.py +203 -17
  13. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/app.js +1 -1
  14. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/settings.js +75 -19
  15. {code_context_control-2.124.0 → code_context_control-2.125.0}/pyproject.toml +1 -1
  16. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_api.py +85 -1
  17. code_context_control-2.125.0/tests/test_mobile_clients.py +543 -0
  18. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_extras.py +4 -2
  19. code_context_control-2.125.0/tests/test_oracle_loopback.py +269 -0
  20. {code_context_control-2.124.0 → code_context_control-2.125.0}/LICENSE +0 -0
  21. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/__init__.py +0 -0
  22. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/_hook_utils.py +0 -0
  23. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/_shell_writes.py +0 -0
  24. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/__init__.py +0 -0
  25. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/common.py +0 -0
  26. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/parser.py +0 -0
  27. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/docs.html +0 -0
  28. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/edits.html +0 -0
  29. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/access.html +0 -0
  30. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/bitbucket.html +0 -0
  31. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/credentials.html +0 -0
  32. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/getting-started.html +0 -0
  33. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/index.html +0 -0
  34. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/masking.html +0 -0
  35. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/oracle.html +0 -0
  36. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/shared.css +0 -0
  37. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/tools.html +0 -0
  38. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/workflow.html +0 -0
  39. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_access_guard.py +0 -0
  40. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_artifact.py +0 -0
  41. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_auto_snapshot.py +0 -0
  42. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_c3_signal.py +0 -0
  43. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_c3read.py +0 -0
  44. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_codex_lifecycle.py +0 -0
  45. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_dispatch.py +0 -0
  46. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_edit_ledger.py +0 -0
  47. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_edit_unlock.py +0 -0
  48. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_filter.py +0 -0
  49. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_ghost_files.py +0 -0
  50. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_pretool_enforce.py +0 -0
  51. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_prompt_recall.py +0 -0
  52. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_read.py +0 -0
  53. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_session_stats.py +0 -0
  54. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_terse_advisor.py +0 -0
  55. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_server.py +0 -0
  56. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/app.js +0 -0
  57. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/add_project.js +0 -0
  58. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/config_editor.js +0 -0
  59. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  60. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_health.js +0 -0
  61. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_panel.js +0 -0
  62. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  63. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  64. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_views.js +0 -0
  65. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/global_search.js +0 -0
  66. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_access.js +0 -0
  67. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_ci.js +0 -0
  68. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  69. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  70. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  71. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_locks.js +0 -0
  72. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  73. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  74. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/modals.js +0 -0
  75. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/project_card.js +0 -0
  76. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/project_tree.js +0 -0
  77. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/session_drawer.js +0 -0
  78. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/settings_modal.js +0 -0
  79. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/sidebar.js +0 -0
  80. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/summary_bar.js +0 -0
  81. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/task_board.js +0 -0
  82. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/toasts.js +0 -0
  83. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/topbar.js +0 -0
  84. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/state.js +0 -0
  85. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui.html +0 -0
  86. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/mcp_proxy.py +0 -0
  87. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/mcp_server.py +0 -0
  88. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/progress.py +0 -0
  89. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/server.py +0 -0
  90. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/__init__.py +0 -0
  91. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/_grants.py +0 -0
  92. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/_helpers.py +0 -0
  93. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/agent.py +0 -0
  94. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/artifacts.py +0 -0
  95. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/bitbucket.py +0 -0
  96. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/ci.py +0 -0
  97. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/compress.py +0 -0
  98. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/credentials.py +0 -0
  99. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/delegate.py +0 -0
  100. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edit.py +0 -0
  101. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edit_verify.py +0 -0
  102. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edits.py +0 -0
  103. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/federate.py +0 -0
  104. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/filter.py +0 -0
  105. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/impact.py +0 -0
  106. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/jira.py +0 -0
  107. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/locks.py +0 -0
  108. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/memory.py +0 -0
  109. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/override.py +0 -0
  110. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/project.py +0 -0
  111. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/read.py +0 -0
  112. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/search.py +0 -0
  113. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/session.py +0 -0
  114. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell.py +0 -0
  115. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_job.py +0 -0
  116. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_nudge.py +0 -0
  117. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_parsers.py +0 -0
  118. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_render.py +0 -0
  119. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/status.py +0 -0
  120. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/tasks.py +0 -0
  121. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/validate.py +0 -0
  122. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/api.js +0 -0
  123. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/app.js +0 -0
  124. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/access.js +0 -0
  125. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/bitbucket.js +0 -0
  126. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/chat.js +0 -0
  127. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/credentials.js +0 -0
  128. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/dashboard.js +0 -0
  129. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/edits.js +0 -0
  130. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/enforcement.js +0 -0
  131. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/instructions.js +0 -0
  132. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/jira.js +0 -0
  133. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/memory.js +0 -0
  134. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/sessions.js +0 -0
  135. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/settings.js +0 -0
  136. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/sidebar.js +0 -0
  137. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/tasks.js +0 -0
  138. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/icons.js +0 -0
  139. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/pm_shared.js +0 -0
  140. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/shared.js +0 -0
  141. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/theme.js +0 -0
  142. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui.html +0 -0
  143. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui_legacy.html +0 -0
  144. {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui_nano.html +0 -0
  145. {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  146. {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/entry_points.txt +0 -0
  147. {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/requires.txt +0 -0
  148. {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/top_level.txt +0 -0
  149. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/__init__.py +0 -0
  150. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/codex_patch.py +0 -0
  151. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/config.py +0 -0
  152. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/host.py +0 -0
  153. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/ide.py +0 -0
  154. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/mcp_toml.py +0 -0
  155. {code_context_control-2.124.0 → code_context_control-2.125.0}/core/web_security.py +0 -0
  156. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/__init__.py +0 -0
  157. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/mcp_oracle.py +0 -0
  158. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/__init__.py +0 -0
  159. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/activity_reporter.py +0 -0
  160. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/api_auth.py +0 -0
  161. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/c3_bridge.py +0 -0
  162. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_engine.py +0 -0
  163. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_store.py +0 -0
  164. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/cross_memory.py +0 -0
  165. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/discovery_audit.py +0 -0
  166. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/federated_graph.py +0 -0
  167. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/health_checker.py +0 -0
  168. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/insight_engine.py +0 -0
  169. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/local_session.py +0 -0
  170. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/memory_reader.py +0 -0
  171. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/memory_writer.py +0 -0
  172. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/project_scanner.py +0 -0
  173. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/review_agent.py +0 -0
  174. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/tool_executor.py +0 -0
  175. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/tool_registry.py +0 -0
  176. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/activity.js +0 -0
  177. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/agents.js +0 -0
  178. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/busy.js +0 -0
  179. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/conversations.js +0 -0
  180. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/input.js +0 -0
  181. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/markdown.js +0 -0
  182. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/send.js +0 -0
  183. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/stream_renderer.js +0 -0
  184. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/toolbar.js +0 -0
  185. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/core.js +0 -0
  186. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/crossgraph.js +0 -0
  187. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/header.js +0 -0
  188. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/insights.js +0 -0
  189. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/projects.js +0 -0
  190. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/qrcode.js +0 -0
  191. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/suggestions.js +0 -0
  192. {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/theme_tabs.js +0 -0
  193. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/__init__.py +0 -0
  194. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/access_guard.py +0 -0
  195. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/access_telemetry.py +0 -0
  196. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/activity_log.py +0 -0
  197. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agent_base.py +0 -0
  198. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agent_locks.py +0 -0
  199. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agents.py +0 -0
  200. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/artifact_defs.py +0 -0
  201. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/artifact_store.py +0 -0
  202. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/atomic_json.py +0 -0
  203. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/auto_memory.py +0 -0
  204. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/background_service.py +0 -0
  205. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/__init__.py +0 -0
  206. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/__init__.py +0 -0
  207. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/aider_polyglot.py +0 -0
  208. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/swe_bench.py +0 -0
  209. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/map_eval.py +0 -0
  210. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/search_eval.py +0 -0
  211. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/shell_eval.py +0 -0
  212. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/benchmark_dashboard.py +0 -0
  213. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bitbucket_client.py +0 -0
  214. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bitbucket_credentials.py +0 -0
  215. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_act.py +0 -0
  216. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_cache.py +0 -0
  217. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_expr.py +0 -0
  218. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_failures.py +0 -0
  219. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_github.py +0 -0
  220. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_impact.py +0 -0
  221. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_intel.py +0 -0
  222. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_runner.py +0 -0
  223. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_workflow.py +0 -0
  224. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/circuit_breaker.py +0 -0
  225. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/claude_md.py +0 -0
  226. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/codex_integration.py +0 -0
  227. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/codex_transcript.py +0 -0
  228. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/compressor.py +0 -0
  229. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/context_snapshot.py +0 -0
  230. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/conversation_store.py +0 -0
  231. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/cred_audit.py +0 -0
  232. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/cred_telemetry.py +0 -0
  233. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/credential_store.py +0 -0
  234. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/dir_map.py +0 -0
  235. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/doc_index.py +0 -0
  236. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_benchmark.py +0 -0
  237. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_evaluator.py +0 -0
  238. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_tasks.py +0 -0
  239. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/edit_ledger.py +0 -0
  240. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/embedding_index.py +0 -0
  241. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/enforcement_policy.py +0 -0
  242. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/error_reporting.py +0 -0
  243. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/file_map.py +0 -0
  244. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/file_memory.py +0 -0
  245. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/git_context.py +0 -0
  246. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/hub_service.py +0 -0
  247. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/index_store.py +0 -0
  248. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/indexer.py +0 -0
  249. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_client.py +0 -0
  250. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_cloud.py +0 -0
  251. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_credentials.py +0 -0
  252. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_data_center.py +0 -0
  253. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_links.py +0 -0
  254. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/lexical_index.py +0 -0
  255. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_activation.py +0 -0
  256. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_mirror.py +0 -0
  257. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_presets.py +0 -0
  258. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory.py +0 -0
  259. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_consolidator.py +0 -0
  260. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_distiller.py +0 -0
  261. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_graph.py +0 -0
  262. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_grounder.py +0 -0
  263. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_queue.py +0 -0
  264. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_scorer.py +0 -0
  265. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/metrics.py +0 -0
  266. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/notifications.py +0 -0
  267. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_bridge.py +0 -0
  268. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_client.py +0 -0
  269. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_credentials.py +0 -0
  270. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/oracle_service.py +0 -0
  271. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/output_filter.py +0 -0
  272. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_grants.py +0 -0
  273. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_policy.py +0 -0
  274. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_requests.py +0 -0
  275. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_wake.py +0 -0
  276. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/parser.py +0 -0
  277. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/project_manager.py +0 -0
  278. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/project_runtime.py +0 -0
  279. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/protocol.py +0 -0
  280. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/proxy_state.py +0 -0
  281. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/repo_map.py +0 -0
  282. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/repo_shape.py +0 -0
  283. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/reranker.py +0 -0
  284. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retention.py +0 -0
  285. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retrieval.py +0 -0
  286. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retrieval_broker.py +0 -0
  287. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/router.py +0 -0
  288. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/runtime.py +0 -0
  289. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/scanner.py +0 -0
  290. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_benchmark.py +0 -0
  291. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_manager.py +0 -0
  292. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_preloader.py +0 -0
  293. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/shell_jobs.py +0 -0
  294. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/shell_output.py +0 -0
  295. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/subprojects.py +0 -0
  296. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/task_store.py +0 -0
  297. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/telemetry.py +0 -0
  298. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/text_index.py +0 -0
  299. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/time_tracker.py +0 -0
  300. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/tool_classifier.py +0 -0
  301. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/transcript_index.py +0 -0
  302. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/validation_cache.py +0 -0
  303. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/vector_store.py +0 -0
  304. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/watcher.py +0 -0
  305. {code_context_control-2.124.0 → code_context_control-2.125.0}/services/win_subprocess.py +0 -0
  306. {code_context_control-2.124.0 → code_context_control-2.125.0}/setup.cfg +0 -0
  307. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_builtin_optout.py +0 -0
  308. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard.py +0 -0
  309. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_meta.py +0 -0
  310. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_shell_project.py +0 -0
  311. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_surfaces.py +0 -0
  312. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_wiring.py +0 -0
  313. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_activity_reporter.py +0 -0
  314. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_agent_locks.py +0 -0
  315. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_aider_polyglot.py +0 -0
  316. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_antigravity_transition.py +0 -0
  317. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_artifact_store.py +0 -0
  318. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_artifact_tool.py +0 -0
  319. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_atomic_json.py +0 -0
  320. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_background_service.py +0 -0
  321. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  322. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_client.py +0 -0
  323. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_config_fallback.py +0 -0
  324. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_credentials.py +0 -0
  325. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_tool.py +0 -0
  326. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_builtin_modes.py +0 -0
  327. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  328. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_c3_shell.py +0 -0
  329. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_chat_poll.py +0 -0
  330. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_act.py +0 -0
  331. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_cache.py +0 -0
  332. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_expr.py +0 -0
  333. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_impact.py +0 -0
  334. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_intel.py +0 -0
  335. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_runner.py +0 -0
  336. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_workflow.py +0 -0
  337. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_circuit_breaker.py +0 -0
  338. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_claude_md_merge.py +0 -0
  339. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_claude_md_updater.py +0 -0
  340. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cli_smoke.py +0 -0
  341. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_codex_mcp_transport.py +0 -0
  342. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_codex_native.py +0 -0
  343. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_compressor_large_file.py +0 -0
  344. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_confirm_guard.py +0 -0
  345. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_confirm_guard_wiring.py +0 -0
  346. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cooccurrence_bounds.py +0 -0
  347. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cred_audit.py +0 -0
  348. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cred_telemetry.py +0 -0
  349. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_login_kind.py +0 -0
  350. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_store.py +0 -0
  351. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_ui_parity.py +0 -0
  352. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credentials_routes.py +0 -0
  353. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credentials_tool.py +0 -0
  354. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_creds_cli_smoke.py +0 -0
  355. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_delegate_cascade.py +0 -0
  356. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_delegate_version_probe.py +0 -0
  357. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_dir_map_c3.py +0 -0
  358. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_e2e_benchmark.py +0 -0
  359. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_ledger_deadline.py +0 -0
  360. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_ledger_hook.py +0 -0
  361. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_locking.py +0 -0
  362. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_normalization.py +0 -0
  363. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_verify.py +0 -0
  364. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_embedding_index_deadlock.py +0 -0
  365. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_flip.py +0 -0
  366. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_policy.py +0 -0
  367. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_routes.py +0 -0
  368. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_env_parser.py +0 -0
  369. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  370. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_federated_graph.py +0 -0
  371. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_file_map_c1.py +0 -0
  372. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_filter_backoff.py +0 -0
  373. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ghost_files.py +0 -0
  374. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ghost_generation.py +0 -0
  375. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_git_branch_awareness.py +0 -0
  376. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_access_guard.py +0 -0
  377. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_codex_compat.py +0 -0
  378. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_dispatch.py +0 -0
  379. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_pretool_enforce.py +0 -0
  380. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_prompt_recall.py +0 -0
  381. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_smoke.py +0 -0
  382. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_state.py +0 -0
  383. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_utf8_stdio.py +0 -0
  384. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_credentials_route.py +0 -0
  385. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_credentials_routes_write.py +0 -0
  386. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_enforcement_routes.py +0 -0
  387. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_inspect_api.py +0 -0
  388. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_locks_routes.py +0 -0
  389. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_oracle_service_routes.py +0 -0
  390. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_override_routes.py +0 -0
  391. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_server_smoke.py +0 -0
  392. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_subproject_links.py +0 -0
  393. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ide_workflow_adaptation.py +0 -0
  394. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  395. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  396. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_init_scan.py +0 -0
  397. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_install_mcp_entrypoint.py +0 -0
  398. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_cli_smoke.py +0 -0
  399. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_client.py +0 -0
  400. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_config_fallback.py +0 -0
  401. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_credentials.py +0 -0
  402. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_links.py +0 -0
  403. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_routes.py +0 -0
  404. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_tool.py +0 -0
  405. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_large_files_c4.py +0 -0
  406. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_lazy_store_init.py +0 -0
  407. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_map_eval.py +0 -0
  408. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_map_telemetry_c0.py +0 -0
  409. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_guard.py +0 -0
  410. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_routes.py +0 -0
  411. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_surfaces.py +0 -0
  412. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_host_guard.py +0 -0
  413. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_memory_timeout.py +0 -0
  414. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_server_smoke.py +0 -0
  415. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_toml.py +0 -0
  416. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_distiller.py +0 -0
  417. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_graph_api.py +0 -0
  418. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_queue.py +0 -0
  419. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_system.py +0 -0
  420. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_override_routes.py +0 -0
  421. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_security_api.py +0 -0
  422. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_no_stale_model_pins.py +0 -0
  423. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_notification_dedup.py +0 -0
  424. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_notification_discipline.py +0 -0
  425. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ollama_credentials.py +0 -0
  426. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_api_auth.py +0 -0
  427. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_apikey_api.py +0 -0
  428. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_c3_bridge.py +0 -0
  429. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_chat_engine.py +0 -0
  430. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_chat_store.py +0 -0
  431. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_discovery_api.py +0 -0
  432. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_discovery_audit.py +0 -0
  433. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_health_probe.py +0 -0
  434. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_local_auth.py +0 -0
  435. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_ollama_bridge.py +0 -0
  436. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_scanner_cache.py +0 -0
  437. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_security_fixes.py +0 -0
  438. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_session_bootstrap.py +0 -0
  439. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_subproject_awareness.py +0 -0
  440. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_ui_bundle.py +0 -0
  441. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_output_filter.py +0 -0
  442. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_grants.py +0 -0
  443. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_grants_mcp.py +0 -0
  444. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_requests.py +0 -0
  445. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_wake.py +0 -0
  446. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_permissions.py +0 -0
  447. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_pm_api.py +0 -0
  448. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_manager.py +0 -0
  449. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_manager_merge.py +0 -0
  450. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_tool.py +0 -0
  451. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_read_coercion.py +0 -0
  452. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_read_edit_parity.py +0 -0
  453. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_related_facts.py +0 -0
  454. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_repo_map.py +0 -0
  455. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_repo_shape.py +0 -0
  456. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_response_boilerplate.py +0 -0
  457. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_retention.py +0 -0
  458. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_review_digest.py +0 -0
  459. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_eval.py +0 -0
  460. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p1.py +0 -0
  461. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p2.py +0 -0
  462. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p2b.py +0 -0
  463. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p3.py +0 -0
  464. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p4.py +0 -0
  465. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p5.py +0 -0
  466. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_service_durability.py +0 -0
  467. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_session_benchmark.py +0 -0
  468. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_session_budget.py +0 -0
  469. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_budget_s1.py +0 -0
  470. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_creds.py +0 -0
  471. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_eval.py +0 -0
  472. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_jobs_s3.py +0 -0
  473. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_nudge_s4.py +0 -0
  474. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_output.py +0 -0
  475. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_parsers_s2.py +0 -0
  476. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_robustness.py +0 -0
  477. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_telemetry_s0.py +0 -0
  478. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_write_ledger.py +0 -0
  479. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_writes.py +0 -0
  480. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_clear_scope.py +0 -0
  481. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_depth_consumers.py +0 -0
  482. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_exclusion.py +0 -0
  483. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_federation.py +0 -0
  484. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_linkage_api.py +0 -0
  485. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subprojects.py +0 -0
  486. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_swe_bench.py +0 -0
  487. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_task_store.py +0 -0
  488. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_task_tool.py +0 -0
  489. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_time_tracker.py +0 -0
  490. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_token_telemetry.py +0 -0
  491. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_token_tracking.py +0 -0
  492. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_tool_registry.py +0 -0
  493. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ui_jsx_syntax.py +0 -0
  494. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_upgrade_and_version.py +0 -0
  495. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_validate.py +0 -0
  496. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_vault_write_guard.py +0 -0
  497. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_version_sync.py +0 -0
  498. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_web_security.py +0 -0
  499. {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_windows_reliability.py +0 -0
  500. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/__init__.py +0 -0
  501. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/backend.py +0 -0
  502. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/main.py +0 -0
  503. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/__init__.py +0 -0
  504. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/benchmark_view.py +0 -0
  505. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/claudemd_view.py +0 -0
  506. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/compress_view.py +0 -0
  507. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/index_view.py +0 -0
  508. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/init_view.py +0 -0
  509. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/mcp_view.py +0 -0
  510. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/optimize_view.py +0 -0
  511. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/pipe_view.py +0 -0
  512. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/projects_view.py +0 -0
  513. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/search_view.py +0 -0
  514. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/session_view.py +0 -0
  515. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/stats.py +0 -0
  516. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/ui_view.py +0 -0
  517. {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.124.0
3
+ Version: 2.125.0
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -380,7 +380,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
380
380
  ## Security & privacy
381
381
 
382
382
  - **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
383
- - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
383
+ - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
384
384
  - **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
385
385
  - **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
386
386
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
@@ -314,7 +314,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
314
314
  ## Security & privacy
315
315
 
316
316
  - **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
317
- - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
317
+ - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
318
318
  - **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
319
319
  - **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
320
320
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
92
92
  # Config
93
93
  CONFIG_DIR = ".c3"
94
94
  CONFIG_FILE = ".c3/config.json"
95
- __version__ = "2.124.0"
95
+ __version__ = "2.125.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.124.0
3
+ Version: 2.125.0
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -380,7 +380,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
380
380
  ## Security & privacy
381
381
 
382
382
  - **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
383
- - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
383
+ - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
384
384
  - **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
385
385
  - **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
386
386
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
@@ -141,6 +141,7 @@ core/mcp_toml.py
141
141
  core/web_security.py
142
142
  oracle/__init__.py
143
143
  oracle/config.py
144
+ oracle/listeners.py
144
145
  oracle/mcp_oracle.py
145
146
  oracle/oracle_server.py
146
147
  oracle/oracle_ui.html
@@ -151,6 +152,7 @@ oracle/services/c3_bridge.py
151
152
  oracle/services/chat_engine.py
152
153
  oracle/services/chat_poll.py
153
154
  oracle/services/chat_store.py
155
+ oracle/services/client_tokens.py
154
156
  oracle/services/cross_memory.py
155
157
  oracle/services/discovery_audit.py
156
158
  oracle/services/federated_graph.py
@@ -410,6 +412,7 @@ tests/test_memory_graph_api.py
410
412
  tests/test_memory_queue.py
411
413
  tests/test_memory_system.py
412
414
  tests/test_mobile_api.py
415
+ tests/test_mobile_clients.py
413
416
  tests/test_mobile_extras.py
414
417
  tests/test_mobile_override_routes.py
415
418
  tests/test_mobile_security_api.py
@@ -426,6 +429,7 @@ tests/test_oracle_discovery_api.py
426
429
  tests/test_oracle_discovery_audit.py
427
430
  tests/test_oracle_health_probe.py
428
431
  tests/test_oracle_local_auth.py
432
+ tests/test_oracle_loopback.py
429
433
  tests/test_oracle_ollama_bridge.py
430
434
  tests/test_oracle_scanner_cache.py
431
435
  tests/test_oracle_security_fixes.py
@@ -11,6 +11,11 @@ DEFAULTS = {
11
11
  "port": 3331,
12
12
  # ── Discovery API (external LLM tool surface, v2.32.0) ──
13
13
  "bind_host": "127.0.0.1", # loopback only by default (was 0.0.0.0)
14
+ # When bind_host is a specific non-loopback address (a Tailscale / LAN
15
+ # IP), ALSO serve the same app on 127.0.0.1 so same-machine clients (the
16
+ # desk tray client, `c3 oracle open`, curl) are not refused. No effect on
17
+ # a loopback or wildcard bind. See oracle/listeners.py.
18
+ "loopback_listener": True,
14
19
  "api_enabled": True, # expose /api/discovery/* REST surface
15
20
  "api_require_auth": True, # require Bearer token on /api/discovery/*
16
21
  "api_max_tier": "action", # cap exposed tools: "read" | "action"
@@ -0,0 +1,210 @@
1
+ """Serve the Oracle on its configured bind host AND on loopback.
2
+
3
+ Why
4
+ ---
5
+ ``bind_host`` exists so a phone on the tailnet can reach the Oracle: set it
6
+ to the Tailscale address and the server listens there — and ONLY there. A
7
+ process on the same machine dialling ``http://127.0.0.1:3331`` was refused,
8
+ which broke every same-box client that reasonably assumes loopback: the
9
+ desk tray client, ``c3 oracle open``'s health probe, the single-instance
10
+ check in ``run_oracle``, curl in a terminal.
11
+
12
+ So when the bind host is a specific non-loopback address, the same Flask app
13
+ is served a second time on ``127.0.0.1``, same port. Same app object means
14
+ the same guards: the Host allowlist (``core.web_security``) always admits
15
+ ``localhost`` / ``127.0.0.1``, the Bearer gates do not care which socket a
16
+ request arrived on, and ``local_session.is_local`` already treats loopback
17
+ as same-machine. A wildcard bind (``0.0.0.0`` / ``::``) already answers on
18
+ loopback, and a loopback bind needs no second socket, so neither gets one.
19
+
20
+ Config: ``loopback_listener`` in ``~/.c3/oracle/config.json`` (default
21
+ true). Off = the pre-v2.125.0 behaviour, one socket on ``bind_host``.
22
+
23
+ Shape
24
+ -----
25
+ ``werkzeug.serving.make_server`` builds one threaded WSGI server per host;
26
+ every listener but the first runs ``serve_forever`` in a daemon thread, the
27
+ first blocks the calling thread exactly as ``app.run`` did. ``shutdown()``
28
+ stops every listener, and ``serve_forever`` calls it on the way out
29
+ (Ctrl-C included) so a secondary socket never outlives the primary.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import ipaddress
35
+ import logging
36
+ import threading
37
+
38
+ LOOPBACK_HOST = "127.0.0.1"
39
+
40
+ # Binds that already answer on loopback, so a second socket would only
41
+ # collide with the first.
42
+ _WILDCARD_HOSTS = frozenset({"", "0.0.0.0", "::", "*"})
43
+
44
+ _log = logging.getLogger("oracle.listeners")
45
+
46
+
47
+ def is_loopback_host(host: str | None) -> bool:
48
+ """True for ``localhost`` and any loopback IP literal (v4 or v6)."""
49
+ h = (host or "").strip().lower()
50
+ if h == "localhost":
51
+ return True
52
+ try:
53
+ return ipaddress.ip_address(h.strip("[]")).is_loopback
54
+ except ValueError:
55
+ return False
56
+
57
+
58
+ def listener_hosts(bind_host: str | None, loopback_listener: bool = True) -> list[str]:
59
+ """The hosts to listen on, primary first.
60
+
61
+ >>> listener_hosts("100.77.40.101")
62
+ ['100.77.40.101', '127.0.0.1']
63
+ >>> listener_hosts("127.0.0.1")
64
+ ['127.0.0.1']
65
+ >>> listener_hosts("0.0.0.0")
66
+ ['0.0.0.0']
67
+ >>> listener_hosts("100.77.40.101", loopback_listener=False)
68
+ ['100.77.40.101']
69
+ """
70
+ primary = (bind_host or "").strip() or LOOPBACK_HOST
71
+ hosts = [primary]
72
+ if not loopback_listener:
73
+ return hosts
74
+ if primary.lower() in _WILDCARD_HOSTS or is_loopback_host(primary):
75
+ return hosts
76
+ hosts.append(LOOPBACK_HOST)
77
+ return hosts
78
+
79
+
80
+ class OracleListeners:
81
+ """One Flask app, N sockets. See the module docstring."""
82
+
83
+ def __init__(self, app, hosts: list[str], port: int, *, threaded: bool = True):
84
+ if not hosts:
85
+ raise ValueError("at least one host is required")
86
+ self.app = app
87
+ self.hosts = list(hosts)
88
+ self.port = int(port)
89
+ self.threaded = threaded
90
+ self.servers: list = []
91
+ # Servers whose serve_forever loop has been entered. socketserver's
92
+ # shutdown() waits on an Event that only serve_forever sets, so
93
+ # calling it on a server that was never served blocks forever.
94
+ self._serving: list = []
95
+ self._threads: list[threading.Thread] = []
96
+ self._lock = threading.Lock()
97
+ self._started = False
98
+ self._stopped = False
99
+
100
+ # ── lifecycle ──────────────────────────────────────────
101
+
102
+ def start(self) -> OracleListeners:
103
+ """Bind every socket. The primary is NOT served yet — call
104
+ :meth:`serve_forever` (blocking) or :meth:`serve_in_background`.
105
+
106
+ A secondary bind that fails (something else already holds the port on
107
+ loopback, say) is logged and skipped; the primary failing raises, as
108
+ ``app.run`` would have.
109
+ """
110
+ from werkzeug.serving import make_server
111
+
112
+ with self._lock:
113
+ if self._started:
114
+ return self
115
+ self._started = True
116
+ primary_host = self.hosts[0]
117
+ primary = make_server(primary_host, self.port, self.app, threaded=self.threaded)
118
+ self.servers.append(primary)
119
+ # A port of 0 asks the OS for one; every secondary must then share it.
120
+ if self.port == 0:
121
+ self.port = int(primary.socket.getsockname()[1])
122
+ for host in self.hosts[1:]:
123
+ try:
124
+ srv = make_server(host, self.port, self.app, threaded=self.threaded)
125
+ except (OSError, SystemExit) as exc:
126
+ # werkzeug turns a failed bind into sys.exit(1) after printing
127
+ # the reason; for a SECONDARY socket that is a warning, not
128
+ # the end of the server.
129
+ _log.warning("loopback listener on %s:%s not started: %s",
130
+ host, self.port, exc)
131
+ continue
132
+ self.servers.append(srv)
133
+ self._serving.append(srv)
134
+ t = threading.Thread(target=srv.serve_forever, daemon=True,
135
+ name=f"oracle-listener-{host}")
136
+ t.start()
137
+ self._threads.append(t)
138
+ return self
139
+
140
+ def serve_forever(self) -> None:
141
+ """Serve the primary listener on the calling thread until shutdown.
142
+
143
+ Every other listener is stopped on the way out, whichever way the
144
+ primary exits (``shutdown()`` from another thread, Ctrl-C, an error).
145
+ """
146
+ if not self._started:
147
+ self.start()
148
+ primary = self.servers[0]
149
+ self._serving.append(primary)
150
+ try:
151
+ primary.serve_forever()
152
+ finally:
153
+ self.shutdown()
154
+
155
+ def serve_in_background(self) -> OracleListeners:
156
+ """Serve the primary on a daemon thread too (tests, embedders)."""
157
+ if not self._started:
158
+ self.start()
159
+ primary = self.servers[0]
160
+ self._serving.append(primary)
161
+ t = threading.Thread(target=primary.serve_forever, daemon=True,
162
+ name=f"oracle-listener-{self.hosts[0]}")
163
+ t.start()
164
+ self._threads.insert(0, t)
165
+ return self
166
+
167
+ def shutdown(self) -> None:
168
+ """Stop every listener and close its socket. Idempotent, and safe to
169
+ call from any thread that is not itself inside ``serve_forever``."""
170
+ with self._lock:
171
+ if self._stopped:
172
+ return
173
+ self._stopped = True
174
+ for srv in self.servers:
175
+ if srv in self._serving:
176
+ try:
177
+ srv.shutdown()
178
+ except Exception:
179
+ pass
180
+ try:
181
+ srv.server_close()
182
+ except Exception:
183
+ pass
184
+ for t in self._threads:
185
+ if t is not threading.current_thread():
186
+ t.join(timeout=5)
187
+
188
+ # ── introspection ──────────────────────────────────────
189
+
190
+ @property
191
+ def addresses(self) -> list[tuple[str, int]]:
192
+ """``(host, port)`` per live listener, primary first."""
193
+ out = []
194
+ for srv in self.servers:
195
+ try:
196
+ name = srv.socket.getsockname()
197
+ out.append((str(name[0]), int(name[1])))
198
+ except OSError:
199
+ continue
200
+ return out
201
+
202
+ @property
203
+ def urls(self) -> list[str]:
204
+ out = []
205
+ for host, port in self.addresses:
206
+ shown = "127.0.0.1" if host in ("0.0.0.0", "") else host
207
+ if ":" in shown and not shown.startswith("["):
208
+ shown = f"[{shown}]"
209
+ out.append(f"http://{shown}:{port}")
210
+ return out
@@ -19,8 +19,9 @@ if str(_PROJECT_ROOT) not in sys.path:
19
19
  from flask import Flask, Response, jsonify, redirect, request, url_for # noqa: E402
20
20
 
21
21
  from oracle.config import ORACLE_DIR, load_config, save_config # noqa: E402
22
+ from oracle.listeners import OracleListeners, listener_hosts # noqa: E402
22
23
  from oracle.mcp_oracle import mcp_url, start_mcp_thread # noqa: E402
23
- from oracle.services import api_auth, local_session # noqa: E402
24
+ from oracle.services import api_auth, client_tokens, local_session # noqa: E402
24
25
  from oracle.services.activity_reporter import ActivityReporter # noqa: E402
25
26
  from oracle.services.api_auth import extract_bearer # noqa: E402
26
27
  from oracle.services.api_auth import verify as verify_api_key # noqa: E402
@@ -233,6 +234,14 @@ def _local_write_guard():
233
234
  path = request.path or ""
234
235
  if not path.startswith("/api/") or path.startswith("/api/discovery"):
235
236
  return None
237
+ # The mobile gateway authenticates itself: both of its blueprints gate
238
+ # EVERY method (GETs included) on a Bearer — the Discovery token or a
239
+ # per-client token (v2.125.0) — and the one route that takes no Bearer,
240
+ # ``POST /api/mobile/clients``, runs its own local-address + bootstrap-key
241
+ # check. Applying this cookie-or-Discovery gate on top would refuse every
242
+ # per-client token before the blueprint ever saw it.
243
+ if path.startswith("/api/mobile/"):
244
+ return None
236
245
  if request.method in ("GET", "HEAD", "OPTIONS"):
237
246
  return None
238
247
  # The bootstrap mint is how a browser ACQUIRES the cookie, so it cannot
@@ -1086,6 +1095,63 @@ def api_apikey_clear():
1086
1095
  return jsonify({"error": str(e)}), 500
1087
1096
 
1088
1097
 
1098
+ # ── Paired clients (dashboard side of oracle/services/client_tokens.py) ──
1099
+ # Cookie-authenticated like the apikey routes: the mutations sit behind
1100
+ # _local_write_guard, the list is a GET and returns no secret. The QR the
1101
+ # Settings tab renders now carries a per-device token minted here, so
1102
+ # rotating the Discovery token no longer un-pairs every phone.
1103
+
1104
+ @app.route("/api/pair/clients", methods=["GET"])
1105
+ def api_pair_clients():
1106
+ """Every paired client (never a hash or token). Signed-in dashboard only
1107
+ — the list names devices, which is more than an anonymous local process
1108
+ should learn."""
1109
+ if not (verify_api_key(extract_bearer(request.headers.get("Authorization")))
1110
+ or local_session.verify(request.cookies.get(local_session.COOKIE_NAME))):
1111
+ return jsonify({"error": "unauthorized"}), 401
1112
+ try:
1113
+ return jsonify({"clients": client_tokens.list_clients()})
1114
+ except Exception as e:
1115
+ return jsonify({"error": str(e)}), 500
1116
+
1117
+
1118
+ @app.route("/api/pair/mobile", methods=["POST"])
1119
+ def api_pair_mobile():
1120
+ """Mint a per-device ``mobile`` client token for the pairing QR.
1121
+
1122
+ Body ``{label?}``. The token is revealed exactly once, here, for the QR;
1123
+ the store keeps only its hash. ``url`` is what the phone should dial —
1124
+ the dashboard overrides it with the Tailscale/LAN address it entered.
1125
+ """
1126
+ data = request.get_json(silent=True) or {}
1127
+ try:
1128
+ row, token = client_tokens.mint("mobile", str(data.get("label") or "phone"))
1129
+ except ValueError as e:
1130
+ return jsonify({"error": str(e)}), 400
1131
+ except Exception as e:
1132
+ return jsonify({"error": str(e)}), 500
1133
+ return jsonify({
1134
+ "client_id": row["client_id"],
1135
+ "kind": row["kind"],
1136
+ "label": row["label"],
1137
+ "created": row["created"],
1138
+ "token": token,
1139
+ "url": request.host_url.rstrip("/"),
1140
+ }), 201
1141
+
1142
+
1143
+ @app.route("/api/pair/clients/<client_id>", methods=["DELETE"])
1144
+ def api_pair_revoke(client_id):
1145
+ """Un-pair one device. Its token fails on the next request."""
1146
+ try:
1147
+ row = client_tokens.revoke(str(client_id))
1148
+ except Exception as e:
1149
+ return jsonify({"error": str(e)}), 500
1150
+ if row is None:
1151
+ return jsonify({"error": "unknown client"}), 404
1152
+ return jsonify({"client": row, "revoked": True})
1153
+
1154
+
1089
1155
  # ── Error handlers ────────────────────────────────────────
1090
1156
  @app.errorhandler(404)
1091
1157
  def not_found(e):
@@ -1215,7 +1281,27 @@ def run_oracle(port: int = None, open_browser: bool = None):
1215
1281
  signed_in = f"{url}/?{local_session.BOOTSTRAP_PARAM}={local_session.mint_code()}"
1216
1282
  threading.Timer(0.8, lambda: webbrowser.open(signed_in)).start()
1217
1283
 
1218
- app.run(host=cfg.get("bind_host", "127.0.0.1"), port=actual_port, debug=False, use_reloader=False)
1284
+ _serve(bind_host, actual_port,
1285
+ loopback_listener=bool(cfg.get("loopback_listener", True)))
1286
+
1287
+
1288
+ def _serve(bind_host: str, port: int, *, loopback_listener: bool = True) -> None:
1289
+ """Block serving ``app`` on ``bind_host`` — and on loopback too when the
1290
+ bind is a specific non-loopback address (``oracle/listeners.py``).
1291
+
1292
+ Replaces ``app.run``: the same threaded werkzeug server, minus the
1293
+ dev-server banner, plus the second socket. Every listener is stopped on
1294
+ the way out (Ctrl-C, service stop, an error) and at interpreter exit.
1295
+ """
1296
+ hosts = listener_hosts(bind_host, loopback_listener)
1297
+ listeners = OracleListeners(app, hosts, port).start()
1298
+ atexit.register(listeners.shutdown)
1299
+ if len(listeners.servers) > 1:
1300
+ print(f"Loopback listener → {listeners.urls[-1]} (same app, same guards)")
1301
+ elif len(hosts) > 1:
1302
+ print(f"Warning: loopback listener on 127.0.0.1:{port} could not start "
1303
+ f"(port in use?) — same-machine clients must dial {bind_host}.")
1304
+ listeners.serve_forever()
1219
1305
 
1220
1306
 
1221
1307
  if __name__ == "__main__":
@@ -1198,19 +1198,25 @@
1198
1198
  <!-- ── Mobile pairing (companion app) ── -->
1199
1199
  <h2 style="font-size:16px;font-weight:600;margin:28px 0 16px">Mobile app <span style="font-size:11px;color:var(--text2);font-weight:400">— pair the C3 companion app</span></h2>
1200
1200
  <div class="card">
1201
- <p style="font-size:12px;color:var(--text2);margin:0 0 14px;line-height:1.5">Scan a QR code from the C3 mobile companion to pair it with this Oracle. The code carries the server URL and the <b>Discovery API token</b> above — treat it like a password, and note that rotating the token un-pairs every client that uses it.</p>
1201
+ <p style="font-size:12px;color:var(--text2);margin:0 0 14px;line-height:1.5">Scan a QR code from the C3 mobile companion to pair it with this Oracle. The code carries the server URL and a <b>per-device token</b> minted for that phone — treat it like a password. Each device gets its own token, so one can be revoked below without touching the others, and rotating the Discovery API token above no longer un-pairs phones.</p>
1202
1202
  <div class="field">
1203
1203
  <label>Server URL (as reachable from the phone)</label>
1204
1204
  <input id="mpUrl" type="text" placeholder="e.g. http://100.x.y.z:3331 (Tailscale IP)">
1205
1205
  <div style="font-size:11px;color:var(--text2);margin-top:6px">Defaults to this page's origin — replace it with the Tailscale/LAN address the phone can reach. Remote access needs <code>bind_host</code> and <code>allowed_hosts</code> set in <code>~/.c3/oracle/config.json</code>.</div>
1206
1206
  </div>
1207
- <div style="display:flex;gap:8px;margin-top:4px">
1207
+ <div style="display:flex;gap:8px;margin-top:4px;flex-wrap:wrap">
1208
1208
  <button class="btn btn-primary" onclick="showMobilePairQr()">Show pairing code</button>
1209
+ <button class="btn btn-ghost" onclick="showDiscoveryPairQr()" title="Legacy: the shared Discovery token, for an app build that predates per-device tokens">Discovery-token code (legacy)</button>
1209
1210
  <button class="btn btn-ghost" id="mpHideBtn" onclick="hideMobilePairQr()" style="display:none">Hide</button>
1210
1211
  </div>
1211
1212
  <div id="mpQrWrap" style="display:none;margin-top:14px">
1212
1213
  <div id="mpQr" style="display:inline-block;background:#fff;padding:14px;border-radius:8px;line-height:0"></div>
1213
- <div style="font-size:11px;color:var(--text2);margin-top:8px">Contains the raw API token — don't screenshot or share it.</div>
1214
+ <div id="mpQrNote" style="font-size:11px;color:var(--text2);margin-top:8px"></div>
1215
+ <div style="font-size:11px;color:var(--text2);margin-top:4px">Contains a raw Bearer token — don't screenshot or share it.</div>
1216
+ </div>
1217
+ <div style="margin-top:16px">
1218
+ <label style="font-size:12px;font-weight:600">Paired devices</label>
1219
+ <div id="mpClients" style="margin-top:6px"></div>
1214
1220
  </div>
1215
1221
  </div>
1216
1222
  </div>
@@ -33,9 +33,8 @@ import threading
33
33
  import time
34
34
  import uuid
35
35
 
36
- from flask import Blueprint, jsonify, request
36
+ from flask import Blueprint, g, jsonify, request
37
37
 
38
- from oracle.services import api_auth
39
38
  from oracle.services.api_auth import extract_bearer
40
39
 
41
40
  #: Capability advertised on ``/api/mobile/info``. Wired into
@@ -115,8 +114,14 @@ def _chat_auth_guard():
115
114
  return None # CORS preflight
116
115
  if not _cfg().get("mobile_api_enabled", True):
117
116
  return jsonify({"error": "mobile API disabled"}), 404
118
- if not api_auth.verify(extract_bearer(request.headers.get("Authorization"))):
117
+ # Same credential set as the gateway proper (Discovery token OR a live
118
+ # per-client token, v2.125.0), resolved by the one shared function so a
119
+ # device paired with a client token can chat too.
120
+ from oracle.services import mobile_api
121
+ who = mobile_api.authenticate(extract_bearer(request.headers.get("Authorization")))
122
+ if who is None:
119
123
  return jsonify({"error": "unauthorized"}), 401
124
+ g.c3_client = who
120
125
  return None
121
126
 
122
127