code-context-control 2.70.0__tar.gz → 2.71.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 (407) hide show
  1. {code_context_control-2.70.0/code_context_control.egg-info → code_context_control-2.71.0}/PKG-INFO +1 -1
  2. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/c3.py +1 -1
  3. {code_context_control-2.70.0 → code_context_control-2.71.0/code_context_control.egg-info}/PKG-INFO +1 -1
  4. {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/SOURCES.txt +1 -0
  5. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/config.py +9 -0
  6. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/mobile_api.py +469 -0
  7. {code_context_control-2.70.0 → code_context_control-2.71.0}/pyproject.toml +1 -1
  8. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_requests.py +195 -2
  9. code_context_control-2.71.0/tests/test_mobile_override_routes.py +747 -0
  10. {code_context_control-2.70.0 → code_context_control-2.71.0}/LICENSE +0 -0
  11. {code_context_control-2.70.0 → code_context_control-2.71.0}/README.md +0 -0
  12. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/__init__.py +0 -0
  13. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/_hook_utils.py +0 -0
  14. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/__init__.py +0 -0
  15. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/common.py +0 -0
  16. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/parser.py +0 -0
  17. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/docs.html +0 -0
  18. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/edits.html +0 -0
  19. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/access.html +0 -0
  20. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/bitbucket.html +0 -0
  21. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/credentials.html +0 -0
  22. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/getting-started.html +0 -0
  23. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/index.html +0 -0
  24. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/masking.html +0 -0
  25. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/oracle.html +0 -0
  26. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/shared.css +0 -0
  27. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/tools.html +0 -0
  28. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/workflow.html +0 -0
  29. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_access_guard.py +0 -0
  30. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_artifact.py +0 -0
  31. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_auto_snapshot.py +0 -0
  32. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_c3_signal.py +0 -0
  33. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_c3read.py +0 -0
  34. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_dispatch.py +0 -0
  35. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_edit_ledger.py +0 -0
  36. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_edit_unlock.py +0 -0
  37. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_filter.py +0 -0
  38. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_ghost_files.py +0 -0
  39. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_pretool_enforce.py +0 -0
  40. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_prompt_recall.py +0 -0
  41. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_read.py +0 -0
  42. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_session_stats.py +0 -0
  43. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_terse_advisor.py +0 -0
  44. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_server.py +0 -0
  45. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/app.js +0 -0
  46. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/add_project.js +0 -0
  47. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/config_editor.js +0 -0
  48. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  49. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_health.js +0 -0
  50. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_panel.js +0 -0
  51. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  52. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  53. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_views.js +0 -0
  54. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/global_search.js +0 -0
  55. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  56. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  57. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_locks.js +0 -0
  58. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  59. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/modals.js +0 -0
  60. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_card.js +0 -0
  61. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_tree.js +0 -0
  62. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/session_drawer.js +0 -0
  63. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/settings_modal.js +0 -0
  64. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/sidebar.js +0 -0
  65. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/summary_bar.js +0 -0
  66. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/task_board.js +0 -0
  67. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/toasts.js +0 -0
  68. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/topbar.js +0 -0
  69. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/state.js +0 -0
  70. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui.html +0 -0
  71. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/mcp_proxy.py +0 -0
  72. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/mcp_server.py +0 -0
  73. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/progress.py +0 -0
  74. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/server.py +0 -0
  75. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/__init__.py +0 -0
  76. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/_helpers.py +0 -0
  77. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/agent.py +0 -0
  78. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/artifacts.py +0 -0
  79. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/bitbucket.py +0 -0
  80. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/compress.py +0 -0
  81. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/credentials.py +0 -0
  82. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/delegate.py +0 -0
  83. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/edit.py +0 -0
  84. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/edits.py +0 -0
  85. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/federate.py +0 -0
  86. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/filter.py +0 -0
  87. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/impact.py +0 -0
  88. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/jira.py +0 -0
  89. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/locks.py +0 -0
  90. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/memory.py +0 -0
  91. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/override.py +0 -0
  92. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/project.py +0 -0
  93. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/read.py +0 -0
  94. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/search.py +0 -0
  95. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/session.py +0 -0
  96. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/shell.py +0 -0
  97. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/status.py +0 -0
  98. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/tasks.py +0 -0
  99. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/validate.py +0 -0
  100. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/api.js +0 -0
  101. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/app.js +0 -0
  102. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/access.js +0 -0
  103. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/bitbucket.js +0 -0
  104. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/chat.js +0 -0
  105. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/credentials.js +0 -0
  106. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/dashboard.js +0 -0
  107. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/edits.js +0 -0
  108. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/enforcement.js +0 -0
  109. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/instructions.js +0 -0
  110. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/jira.js +0 -0
  111. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/memory.js +0 -0
  112. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/sessions.js +0 -0
  113. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/settings.js +0 -0
  114. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/sidebar.js +0 -0
  115. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/tasks.js +0 -0
  116. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/icons.js +0 -0
  117. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/pm_shared.js +0 -0
  118. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/shared.js +0 -0
  119. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/theme.js +0 -0
  120. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui.html +0 -0
  121. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui_legacy.html +0 -0
  122. {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui_nano.html +0 -0
  123. {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  124. {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/entry_points.txt +0 -0
  125. {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/requires.txt +0 -0
  126. {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/top_level.txt +0 -0
  127. {code_context_control-2.70.0 → code_context_control-2.71.0}/core/__init__.py +0 -0
  128. {code_context_control-2.70.0 → code_context_control-2.71.0}/core/config.py +0 -0
  129. {code_context_control-2.70.0 → code_context_control-2.71.0}/core/ide.py +0 -0
  130. {code_context_control-2.70.0 → code_context_control-2.71.0}/core/mcp_toml.py +0 -0
  131. {code_context_control-2.70.0 → code_context_control-2.71.0}/core/web_security.py +0 -0
  132. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/__init__.py +0 -0
  133. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/mcp_oracle.py +0 -0
  134. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/oracle_server.py +0 -0
  135. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/oracle_ui.html +0 -0
  136. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/__init__.py +0 -0
  137. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/activity_reporter.py +0 -0
  138. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/api_auth.py +0 -0
  139. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/c3_bridge.py +0 -0
  140. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/chat_engine.py +0 -0
  141. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/chat_store.py +0 -0
  142. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/cross_memory.py +0 -0
  143. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/discovery_audit.py +0 -0
  144. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/federated_graph.py +0 -0
  145. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/health_checker.py +0 -0
  146. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/insight_engine.py +0 -0
  147. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/local_session.py +0 -0
  148. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/memory_reader.py +0 -0
  149. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/memory_writer.py +0 -0
  150. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/project_scanner.py +0 -0
  151. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/review_agent.py +0 -0
  152. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/tool_executor.py +0 -0
  153. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/tool_registry.py +0 -0
  154. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/activity.js +0 -0
  155. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/agents.js +0 -0
  156. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/app.js +0 -0
  157. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/busy.js +0 -0
  158. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/conversations.js +0 -0
  159. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/input.js +0 -0
  160. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/markdown.js +0 -0
  161. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/send.js +0 -0
  162. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/stream_renderer.js +0 -0
  163. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/toolbar.js +0 -0
  164. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/core.js +0 -0
  165. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/crossgraph.js +0 -0
  166. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/header.js +0 -0
  167. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/insights.js +0 -0
  168. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/projects.js +0 -0
  169. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/qrcode.js +0 -0
  170. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/settings.js +0 -0
  171. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/suggestions.js +0 -0
  172. {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/theme_tabs.js +0 -0
  173. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/__init__.py +0 -0
  174. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/access_guard.py +0 -0
  175. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/access_telemetry.py +0 -0
  176. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/activity_log.py +0 -0
  177. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agent_base.py +0 -0
  178. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agent_locks.py +0 -0
  179. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agents.py +0 -0
  180. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/artifact_defs.py +0 -0
  181. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/artifact_store.py +0 -0
  182. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/auto_memory.py +0 -0
  183. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/__init__.py +0 -0
  184. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/__init__.py +0 -0
  185. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/aider_polyglot.py +0 -0
  186. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/swe_bench.py +0 -0
  187. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/benchmark_dashboard.py +0 -0
  188. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bitbucket_client.py +0 -0
  189. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bitbucket_credentials.py +0 -0
  190. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/circuit_breaker.py +0 -0
  191. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/claude_md.py +0 -0
  192. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/compressor.py +0 -0
  193. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/context_snapshot.py +0 -0
  194. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/conversation_store.py +0 -0
  195. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/credential_store.py +0 -0
  196. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/doc_index.py +0 -0
  197. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_benchmark.py +0 -0
  198. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_evaluator.py +0 -0
  199. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_tasks.py +0 -0
  200. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/edit_ledger.py +0 -0
  201. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/embedding_index.py +0 -0
  202. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/enforcement_policy.py +0 -0
  203. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/error_reporting.py +0 -0
  204. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/file_memory.py +0 -0
  205. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/git_context.py +0 -0
  206. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/hub_service.py +0 -0
  207. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/indexer.py +0 -0
  208. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_client.py +0 -0
  209. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_cloud.py +0 -0
  210. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_credentials.py +0 -0
  211. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_data_center.py +0 -0
  212. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_links.py +0 -0
  213. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_activation.py +0 -0
  214. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_mirror.py +0 -0
  215. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_presets.py +0 -0
  216. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory.py +0 -0
  217. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_consolidator.py +0 -0
  218. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_distiller.py +0 -0
  219. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_graph.py +0 -0
  220. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_grounder.py +0 -0
  221. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_queue.py +0 -0
  222. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_scorer.py +0 -0
  223. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/metrics.py +0 -0
  224. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/notifications.py +0 -0
  225. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_bridge.py +0 -0
  226. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_client.py +0 -0
  227. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_credentials.py +0 -0
  228. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/output_filter.py +0 -0
  229. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_grants.py +0 -0
  230. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_policy.py +0 -0
  231. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/parser.py +0 -0
  232. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/project_manager.py +0 -0
  233. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/project_runtime.py +0 -0
  234. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/protocol.py +0 -0
  235. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/proxy_state.py +0 -0
  236. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/repo_map.py +0 -0
  237. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/retention.py +0 -0
  238. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/retrieval_broker.py +0 -0
  239. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/router.py +0 -0
  240. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/runtime.py +0 -0
  241. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/scanner.py +0 -0
  242. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_benchmark.py +0 -0
  243. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_manager.py +0 -0
  244. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_preloader.py +0 -0
  245. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/subprojects.py +0 -0
  246. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/task_store.py +0 -0
  247. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/telemetry.py +0 -0
  248. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/text_index.py +0 -0
  249. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/time_tracker.py +0 -0
  250. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/tool_classifier.py +0 -0
  251. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/transcript_index.py +0 -0
  252. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/validation_cache.py +0 -0
  253. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/vector_store.py +0 -0
  254. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/watcher.py +0 -0
  255. {code_context_control-2.70.0 → code_context_control-2.71.0}/services/win_subprocess.py +0 -0
  256. {code_context_control-2.70.0 → code_context_control-2.71.0}/setup.cfg +0 -0
  257. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_builtin_optout.py +0 -0
  258. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard.py +0 -0
  259. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_meta.py +0 -0
  260. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_shell_project.py +0 -0
  261. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_surfaces.py +0 -0
  262. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_wiring.py +0 -0
  263. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_activity_reporter.py +0 -0
  264. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_agent_locks.py +0 -0
  265. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_aider_polyglot.py +0 -0
  266. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_antigravity_transition.py +0 -0
  267. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_artifact_store.py +0 -0
  268. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_artifact_tool.py +0 -0
  269. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  270. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_client.py +0 -0
  271. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_config_fallback.py +0 -0
  272. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_credentials.py +0 -0
  273. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_tool.py +0 -0
  274. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  275. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_c3_shell.py +0 -0
  276. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_circuit_breaker.py +0 -0
  277. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_claude_md_merge.py +0 -0
  278. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_cli_smoke.py +0 -0
  279. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_compressor_large_file.py +0 -0
  280. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credential_store.py +0 -0
  281. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credentials_routes.py +0 -0
  282. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credentials_tool.py +0 -0
  283. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_creds_cli_smoke.py +0 -0
  284. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_delegate_cascade.py +0 -0
  285. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_delegate_version_probe.py +0 -0
  286. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_e2e_benchmark.py +0 -0
  287. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_ledger_hook.py +0 -0
  288. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_locking.py +0 -0
  289. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_normalization.py +0 -0
  290. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_embedding_index_deadlock.py +0 -0
  291. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_flip.py +0 -0
  292. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_policy.py +0 -0
  293. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_routes.py +0 -0
  294. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_federated_graph.py +0 -0
  295. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_filter_backoff.py +0 -0
  296. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ghost_files.py +0 -0
  297. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ghost_generation.py +0 -0
  298. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_git_branch_awareness.py +0 -0
  299. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_access_guard.py +0 -0
  300. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_dispatch.py +0 -0
  301. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_pretool_enforce.py +0 -0
  302. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_prompt_recall.py +0 -0
  303. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_smoke.py +0 -0
  304. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_state.py +0 -0
  305. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_utf8_stdio.py +0 -0
  306. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_credentials_route.py +0 -0
  307. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_credentials_routes_write.py +0 -0
  308. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_enforcement_routes.py +0 -0
  309. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_inspect_api.py +0 -0
  310. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_locks_routes.py +0 -0
  311. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_server_smoke.py +0 -0
  312. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_subproject_links.py +0 -0
  313. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ide_workflow_adaptation.py +0 -0
  314. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_init_scan.py +0 -0
  315. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_install_mcp_entrypoint.py +0 -0
  316. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_cli_smoke.py +0 -0
  317. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_client.py +0 -0
  318. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_config_fallback.py +0 -0
  319. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_credentials.py +0 -0
  320. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_links.py +0 -0
  321. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_routes.py +0 -0
  322. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_tool.py +0 -0
  323. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_lazy_store_init.py +0 -0
  324. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_guard.py +0 -0
  325. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_routes.py +0 -0
  326. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_surfaces.py +0 -0
  327. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_host_guard.py +0 -0
  328. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_memory_timeout.py +0 -0
  329. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_server_smoke.py +0 -0
  330. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_toml.py +0 -0
  331. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_distiller.py +0 -0
  332. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_graph_api.py +0 -0
  333. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_queue.py +0 -0
  334. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_system.py +0 -0
  335. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mobile_api.py +0 -0
  336. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mobile_security_api.py +0 -0
  337. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_no_stale_model_pins.py +0 -0
  338. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_notification_dedup.py +0 -0
  339. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_notification_discipline.py +0 -0
  340. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ollama_credentials.py +0 -0
  341. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_api_auth.py +0 -0
  342. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_apikey_api.py +0 -0
  343. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_c3_bridge.py +0 -0
  344. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_chat_engine.py +0 -0
  345. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_chat_store.py +0 -0
  346. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_api.py +0 -0
  347. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_audit.py +0 -0
  348. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_local_auth.py +0 -0
  349. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_ollama_bridge.py +0 -0
  350. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_scanner_cache.py +0 -0
  351. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_security_fixes.py +0 -0
  352. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_session_bootstrap.py +0 -0
  353. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_subproject_awareness.py +0 -0
  354. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_ui_bundle.py +0 -0
  355. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_output_filter.py +0 -0
  356. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_override_grants.py +0 -0
  357. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_override_requests.py +0 -0
  358. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_permissions.py +0 -0
  359. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_pm_api.py +0 -0
  360. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_manager.py +0 -0
  361. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_manager_merge.py +0 -0
  362. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_tool.py +0 -0
  363. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_read_coercion.py +0 -0
  364. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_read_edit_parity.py +0 -0
  365. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_related_facts.py +0 -0
  366. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_repo_map.py +0 -0
  367. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_response_boilerplate.py +0 -0
  368. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_retention.py +0 -0
  369. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_review_digest.py +0 -0
  370. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_service_durability.py +0 -0
  371. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_session_benchmark.py +0 -0
  372. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_session_budget.py +0 -0
  373. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_shell_creds.py +0 -0
  374. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_shell_robustness.py +0 -0
  375. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subproject_exclusion.py +0 -0
  376. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subproject_federation.py +0 -0
  377. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subprojects.py +0 -0
  378. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_swe_bench.py +0 -0
  379. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_task_store.py +0 -0
  380. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_task_tool.py +0 -0
  381. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_time_tracker.py +0 -0
  382. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_token_telemetry.py +0 -0
  383. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_tool_registry.py +0 -0
  384. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_upgrade_and_version.py +0 -0
  385. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_validate.py +0 -0
  386. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_vault_write_guard.py +0 -0
  387. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_version_sync.py +0 -0
  388. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_web_security.py +0 -0
  389. {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_windows_reliability.py +0 -0
  390. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/__init__.py +0 -0
  391. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/backend.py +0 -0
  392. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/main.py +0 -0
  393. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/__init__.py +0 -0
  394. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/benchmark_view.py +0 -0
  395. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/claudemd_view.py +0 -0
  396. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/compress_view.py +0 -0
  397. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/index_view.py +0 -0
  398. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/init_view.py +0 -0
  399. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/mcp_view.py +0 -0
  400. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/optimize_view.py +0 -0
  401. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/pipe_view.py +0 -0
  402. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/projects_view.py +0 -0
  403. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/search_view.py +0 -0
  404. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/session_view.py +0 -0
  405. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/stats.py +0 -0
  406. {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/ui_view.py +0 -0
  407. {code_context_control-2.70.0 → code_context_control-2.71.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.70.0
3
+ Version: 2.71.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
@@ -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.70.0"
95
+ __version__ = "2.71.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.70.0
3
+ Version: 2.71.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
@@ -330,6 +330,7 @@ tests/test_memory_graph_api.py
330
330
  tests/test_memory_queue.py
331
331
  tests/test_memory_system.py
332
332
  tests/test_mobile_api.py
333
+ tests/test_mobile_override_routes.py
333
334
  tests/test_mobile_security_api.py
334
335
  tests/test_no_stale_model_pins.py
335
336
  tests/test_notification_dedup.py
@@ -37,6 +37,15 @@ DEFAULTS = {
37
37
  # A global-scope access rule applies to EVERY project on this machine —
38
38
  # blast radius beyond the projects the token can even enumerate.
39
39
  "mobile_access_global_scope": False,
40
+ # ── Override Requests (docs/override-requests.md §3.2) ──
41
+ # The approval inbox. `override` serves the read routes (list / one /
42
+ # policy); `override_write` is what turns a phone tap into a real grant,
43
+ # so it is the switch that matters. Both default ON to match the access
44
+ # pair — the feature is already off by default one layer down, in the
45
+ # PROJECT policy (`override.enabled: false`, every layer false), which is
46
+ # what decides whether a request can exist at all.
47
+ "mobile_override_enabled": True, # read: request list / one / policy
48
+ "mobile_override_write": True, # decide / mute / policy edit
40
49
  # Second, tighter budget for security mutations, on top of the shared
41
50
  # api_rate_limit_per_min bucket. 0 = share the main bucket only.
42
51
  "mobile_security_rate_limit_per_min": 12,
@@ -66,6 +66,7 @@ CAPABILITIES = [
66
66
  "credentials", "credentials_write",
67
67
  "access", "access_write",
68
68
  "enforcement", "enforcement_write",
69
+ "override", "override_write",
69
70
  ]
70
71
 
71
72
  # capability -> config key that gates it. Absent from this map = always on.
@@ -76,6 +77,8 @@ _CAPABILITY_SWITCHES = {
76
77
  "access_write": "mobile_access_write",
77
78
  "enforcement": "mobile_access_enabled",
78
79
  "enforcement_write": "mobile_access_write",
80
+ "override": "mobile_override_enabled",
81
+ "override_write": "mobile_override_write",
79
82
  }
80
83
 
81
84
  FEED_TYPES = ("notification", "activity", "edit", "session_stat")
@@ -1616,3 +1619,469 @@ def mobile_enforcement_set():
1616
1619
  pass
1617
1620
  _gw_audit("mobile.enforcement.set_mode", {"mode": result["mode"]})
1618
1621
  return jsonify(result)
1622
+
1623
+
1624
+ # ── Override Requests (docs/override-requests.md §8) ──────
1625
+ # The approval inbox: the agent asks, the phone answers, the answer is a
1626
+ # narrow time-boxed grant rather than a policy change. These routes are
1627
+ # deliberately THIN — every decision, refusal and grant-mint lives in
1628
+ # services/override_requests.py so the CLI (`c3 override`) and this surface
1629
+ # cannot drift apart on the one check that matters (the typed confirmation).
1630
+ #
1631
+ # The wire contract is frozen in §3.3 and re-asserted by
1632
+ # tests/test_mobile_override_routes.py: the request row is serialised through
1633
+ # an explicit allowlist, not `dict(row)`, so an internal field added later
1634
+ # (e.g. `path_key`) cannot silently start crossing the network.
1635
+
1636
+ #: §3.3 field order, verbatim. The mobile client reads these names.
1637
+ _OVERRIDE_FIELDS = (
1638
+ "id", "project_path", "session_id", "created_at", "expires_at", "status",
1639
+ "layer", "rule", "rule_class", "scope", "tool", "op", "path", "refusal",
1640
+ "justification", "resolved_at", "decided_by", "decision_note",
1641
+ )
1642
+
1643
+ #: Decisions made here are always attributed to the phone (§3.3 decided_by).
1644
+ _DECIDED_BY = "mobile"
1645
+
1646
+
1647
+ def _override_row(row: dict) -> dict:
1648
+ """One request row for the wire. Allowlisted, never the raw dict.
1649
+
1650
+ ``path_key`` is deliberately absent: it is a canonicalised local
1651
+ filesystem identity used for grant matching, and the phone already gets
1652
+ the human-readable ``path``.
1653
+ """
1654
+ out = {k: row.get(k) for k in _OVERRIDE_FIELDS}
1655
+ # Decision extras — present only once decided, so a pending card is exactly
1656
+ # the §3.3 shape and nothing more.
1657
+ for extra in ("grant_id", "grant_mode", "muted"):
1658
+ if row.get(extra) is not None:
1659
+ out[extra] = row.get(extra)
1660
+ return out
1661
+
1662
+
1663
+ def _override_audit(action: str, row: dict, *, confirmed: bool = False,
1664
+ detail_extra: dict | None = None) -> None:
1665
+ """Identifiers and rule globs only — never the justification.
1666
+
1667
+ The justification is agent-supplied untrusted text (§3.3); copying it into
1668
+ the activity log would put attacker-controlled prose into a surface the
1669
+ feed ships verbatim to every client.
1670
+ """
1671
+ detail = {"kind": "override", "action": action,
1672
+ "request_id": row.get("id", ""),
1673
+ "session_id": row.get("session_id", ""),
1674
+ "rule": row.get("rule", ""),
1675
+ "rule_class": row.get("rule_class", ""),
1676
+ "tool": row.get("tool", ""), "op": row.get("op", ""),
1677
+ "via": "oracle-mobile", "caller": _caller(),
1678
+ "confirmed": bool(confirmed)}
1679
+ detail.update(detail_extra or {})
1680
+ project = row.get("project_path") or ""
1681
+ if not project:
1682
+ return
1683
+ try:
1684
+ from services.activity_log import ActivityLog
1685
+ ActivityLog(str(project)).log("access_action", dict(detail))
1686
+ except Exception:
1687
+ pass
1688
+ try:
1689
+ from services.edit_ledger import EditLedger
1690
+ EditLedger(str(project)).log_edit(
1691
+ file=f"override://{row.get('id', '')}",
1692
+ change_type=f"override_{action}",
1693
+ summary=(f"{action} override {row.get('id', '')} "
1694
+ f"({row.get('rule', '')}) via mobile"),
1695
+ tags=["override", action], detail=dict(detail))
1696
+ except Exception:
1697
+ pass
1698
+
1699
+
1700
+ def _override_write_gate():
1701
+ """Capability + rate budget for every override mutation."""
1702
+ gate = _feature_or_404("override") or _feature_or_404("override_write")
1703
+ if gate:
1704
+ return gate
1705
+ return _security_gate()
1706
+
1707
+
1708
+ @bp.route("/overrides", methods=["GET"])
1709
+ def mobile_overrides_list():
1710
+ """Pending + recently-decided requests, newest first.
1711
+
1712
+ ``project`` is OPTIONAL and omitting it means every project (§15.5): the
1713
+ entire point of the inbox is answering while away from the desk, and a
1714
+ phone that must first pick the right project to discover it has a pending
1715
+ approval defeats that.
1716
+ """
1717
+ gate = _feature_or_404("override")
1718
+ if gate:
1719
+ return gate
1720
+ from services import override_requests as orq
1721
+ raw_project = (request.args.get("project") or "").strip()
1722
+ project_path = ""
1723
+ if raw_project:
1724
+ # A named project must still be one this token can see.
1725
+ resolved, err = _project_or_404(raw_project)
1726
+ if err:
1727
+ return err
1728
+ project_path = str(resolved)
1729
+ status = (request.args.get("status") or "").strip().lower()
1730
+ try:
1731
+ limit = int(request.args.get("limit") or _DEFAULT_LIMIT)
1732
+ except (TypeError, ValueError):
1733
+ limit = _DEFAULT_LIMIT
1734
+ limit = max(1, min(limit, _MAX_LIMIT))
1735
+ try:
1736
+ rows = orq.list_requests(project_path=project_path, status=status,
1737
+ limit=limit)
1738
+ except Exception as exc:
1739
+ return _svc_error(exc)
1740
+ if not raw_project:
1741
+ # Cross-project listing must not leak rows for projects this gateway
1742
+ # does not serve — the store is one file across every project on the
1743
+ # machine, including ones the scanner has since dropped.
1744
+ rows = [r for r in rows
1745
+ if _resolve_registered(str(r.get("project_path") or "")) is not None]
1746
+ return jsonify({
1747
+ "requests": [_override_row(r) for r in rows],
1748
+ "count": len(rows),
1749
+ "project": project_path,
1750
+ "status": status,
1751
+ "limit": limit,
1752
+ })
1753
+
1754
+
1755
+ @bp.route("/overrides/policy", methods=["GET"])
1756
+ def mobile_overrides_policy_get():
1757
+ """The effective (already merged + clamped) `override` section (§3.1).
1758
+
1759
+ Registered BEFORE `/overrides/<id>` would otherwise catch it — Flask
1760
+ matches static rules ahead of converters, but the ordering is made
1761
+ explicit here so a later refactor cannot turn `policy` into a request id.
1762
+ """
1763
+ gate = _feature_or_404("override")
1764
+ if gate:
1765
+ return gate
1766
+ from services import override_policy as opol
1767
+ resolved, err = _project_or_404(request.args.get("project") or "")
1768
+ if err:
1769
+ return err
1770
+ policy = opol.resolve(str(resolved))
1771
+ return jsonify({
1772
+ "path": str(resolved),
1773
+ "policy": policy.as_dict(),
1774
+ "layers": list(opol.LAYER_KEYS),
1775
+ "typed_confirm_layers": sorted(opol.TYPED_CONFIRM_LAYERS),
1776
+ "hard_max_ttl_s": opol.HARD_MAX_TTL_S,
1777
+ "defaults": {"max_ttl_s": opol.DEFAULTS["max_ttl_s"],
1778
+ "default_uses": opol.DEFAULTS["default_uses"]},
1779
+ "writable": bool(_cfg().get("mobile_override_write", True)),
1780
+ # Verbatim from the spec: policy is a floor, not a promise. Global and
1781
+ # project merge by TIGHTENING, so a layer shown true here can still be
1782
+ # false in effect if the other scope forbids it.
1783
+ "coverage_note": (
1784
+ "Project and global policy merge by tightening only: a layer is "
1785
+ "escalatable iff both scopes allow it, and the numeric limits take "
1786
+ "the smaller value. Grants are honoured on the PreToolUse hook "
1787
+ "surface; the c3_* MCP tools do not consult them yet (P2a)."
1788
+ ),
1789
+ })
1790
+
1791
+
1792
+ @bp.route("/overrides/policy", methods=["POST"])
1793
+ def mobile_overrides_policy_set():
1794
+ """Edit the project's `override` section.
1795
+
1796
+ Widening — enabling the feature, turning a layer on, raising a ceiling, or
1797
+ allowing session grants — needs a typed confirmation. Tightening never
1798
+ does: making the guard stricter is always allowed to be one tap.
1799
+ """
1800
+ gate = _override_write_gate()
1801
+ if gate:
1802
+ return gate
1803
+ from services import override_policy as opol
1804
+ data = request.get_json(silent=True) or {}
1805
+ resolved, err = _project_or_404(data.get("project") or "")
1806
+ if err:
1807
+ return err
1808
+
1809
+ section = data.get("override")
1810
+ if not isinstance(section, dict):
1811
+ return jsonify({"error": "body needs an 'override' object"}), 400
1812
+ unknown = sorted(set(section) - set(opol.DEFAULTS))
1813
+ if unknown:
1814
+ # §3.1: unknown keys are a hard error, never a silent no-op.
1815
+ return jsonify({
1816
+ "error": f"unknown override key(s): {', '.join(unknown)}",
1817
+ "valid": sorted(opol.DEFAULTS),
1818
+ }), 400
1819
+ layers = section.get("layers")
1820
+ if layers is not None:
1821
+ if not isinstance(layers, dict):
1822
+ return jsonify({"error": "'layers' must be an object"}), 400
1823
+ bad = sorted(set(layers) - set(opol.LAYER_KEYS))
1824
+ if bad:
1825
+ return jsonify({"error": f"unknown layer(s): {', '.join(bad)}",
1826
+ "valid": list(opol.LAYER_KEYS)}), 400
1827
+
1828
+ current = opol.resolve(str(resolved))
1829
+ widened = _override_widenings(current, section)
1830
+ if widened and not _confirmed(data, "widen"):
1831
+ return jsonify({
1832
+ "error": "this widens what a phone tap can approve: "
1833
+ + ", ".join(widened),
1834
+ "widens": widened,
1835
+ "needs_confirmation": True, "confirm_with": "widen",
1836
+ }), 400
1837
+
1838
+ try:
1839
+ result = _write_override_section(resolved, section)
1840
+ except (OSError, ValueError) as exc:
1841
+ return _svc_error(exc)
1842
+
1843
+ _override_audit("policy_set",
1844
+ {"id": "", "project_path": str(resolved), "rule": ""},
1845
+ confirmed=bool(widened),
1846
+ detail_extra={"widens": widened,
1847
+ "keys": sorted(section)})
1848
+ _gw_audit("mobile.override.policy_set",
1849
+ {"keys": sorted(section), "widens": widened})
1850
+ effective = opol.resolve(str(resolved))
1851
+ return jsonify({"path": str(resolved), "written": result,
1852
+ "policy": effective.as_dict(), "widened": widened})
1853
+
1854
+
1855
+ def _override_widenings(current, section: dict) -> list:
1856
+ """Which requested changes LOOSEN the policy. Names, for the challenge."""
1857
+ out = []
1858
+ if section.get("enabled") and not current.enabled:
1859
+ out.append("enabled")
1860
+ for key, want in (section.get("layers") or {}).items():
1861
+ if want and not current.layers.get(key, False):
1862
+ out.append(f"layers.{key}")
1863
+ for key in ("max_ttl_s", "default_uses", "request_ttl_s",
1864
+ "max_pending_per_session", "max_requests_per_hour"):
1865
+ if key in section:
1866
+ try:
1867
+ if int(section[key]) > int(getattr(current, key)):
1868
+ out.append(key)
1869
+ except (TypeError, ValueError):
1870
+ pass
1871
+ if section.get("allow_session_grants") and not current.allow_session_grants:
1872
+ out.append("allow_session_grants")
1873
+ return out
1874
+
1875
+
1876
+ def _write_override_section(project: Path, section: dict) -> dict:
1877
+ """Merge *section* into the project's `.c3/config.json` `override` block.
1878
+
1879
+ Project scope only. A global override policy governs every project on the
1880
+ machine, and the phone has no affordance for reviewing that blast radius —
1881
+ same call `mobile_enforcement_set` makes about machine-wide discipline.
1882
+ """
1883
+ cfg_file = Path(project) / ".c3" / "config.json"
1884
+ cfg_file.parent.mkdir(parents=True, exist_ok=True)
1885
+ cfg = {}
1886
+ if cfg_file.is_file():
1887
+ try:
1888
+ cfg = json.loads(cfg_file.read_text(encoding="utf-8")) or {}
1889
+ except ValueError as exc:
1890
+ raise ValueError(f"project config is not valid JSON: {exc}") from exc
1891
+ if not isinstance(cfg, dict):
1892
+ raise ValueError("project config is not a JSON object")
1893
+ block = dict(cfg.get("override") or {})
1894
+ for key, value in section.items():
1895
+ if key == "layers":
1896
+ merged = dict(block.get("layers") or {})
1897
+ merged.update({k: bool(v) for k, v in (value or {}).items()})
1898
+ block["layers"] = merged
1899
+ else:
1900
+ block[key] = value
1901
+ cfg["override"] = block
1902
+ tmp = cfg_file.with_name(f"{cfg_file.name}.tmp")
1903
+ tmp.write_text(json.dumps(cfg, indent=2, ensure_ascii=False),
1904
+ encoding="utf-8")
1905
+ tmp.replace(cfg_file)
1906
+ return block
1907
+
1908
+
1909
+ @bp.route("/overrides/<request_id>", methods=["GET"])
1910
+ def mobile_override_get(request_id):
1911
+ """One request, full context."""
1912
+ gate = _feature_or_404("override")
1913
+ if gate:
1914
+ return gate
1915
+ from services import override_requests as orq
1916
+ row = orq.get(str(request_id))
1917
+ if row is None:
1918
+ return jsonify({"error": "unknown request"}), 404
1919
+ if _resolve_registered(str(row.get("project_path") or "")) is None:
1920
+ # Same refusal as an unknown id: whether a request exists for a project
1921
+ # this gateway does not serve is not this token's business.
1922
+ return jsonify({"error": "unknown request"}), 404
1923
+ from services import override_policy as opol
1924
+ policy = opol.resolve(str(row.get("project_path") or "."))
1925
+ return jsonify({
1926
+ "request": _override_row(row),
1927
+ # What approving would actually cost, so the card can say it BEFORE
1928
+ # the tap rather than reporting a clamp afterwards.
1929
+ "policy": {
1930
+ "max_ttl_s": policy.max_ttl_s,
1931
+ "default_uses": policy.default_uses,
1932
+ "allow_session_grants": policy.allow_session_grants,
1933
+ "escalatable": policy.escalatable(str(row.get("rule_class") or "")),
1934
+ },
1935
+ "needs_typed_confirm": (
1936
+ str(row.get("rule_class") or "") in opol.TYPED_CONFIRM_LAYERS),
1937
+ "confirm_with": (
1938
+ row.get("rule") if str(row.get("rule_class") or "")
1939
+ in opol.TYPED_CONFIRM_LAYERS else ""),
1940
+ })
1941
+
1942
+
1943
+ @bp.route("/overrides/<request_id>/decide", methods=["POST"])
1944
+ def mobile_override_decide(request_id):
1945
+ """Approve (minting a grant) or deny one request.
1946
+
1947
+ The typed-confirmation challenges are computed here for the CLIENT's
1948
+ benefit — the authoritative check is inside ``override_requests.decide``,
1949
+ which refuses regardless of what this route believes. Two places compute
1950
+ it; only one enforces it.
1951
+ """
1952
+ gate = _override_write_gate()
1953
+ if gate:
1954
+ return gate
1955
+ from services import override_policy as opol
1956
+ from services import override_requests as orq
1957
+ data = request.get_json(silent=True) or {}
1958
+ decision = str(data.get("decision") or "").strip().lower()
1959
+ if decision not in (orq.DECISION_APPROVE, orq.DECISION_DENY):
1960
+ return jsonify({"error": "decision must be 'approve' or 'deny'"}), 400
1961
+ mode = str(data.get("mode") or orq.MODE_ONCE).strip().lower()
1962
+ if mode not in orq.MODES:
1963
+ return jsonify({"error": f"mode must be one of: "
1964
+ f"{', '.join(orq.MODES)}"}), 400
1965
+
1966
+ row = orq.get(str(request_id))
1967
+ if row is None or _resolve_registered(str(row.get("project_path") or "")) is None:
1968
+ return jsonify({"error": "unknown request"}), 404
1969
+ if row.get("status") != orq.STATUS_PENDING:
1970
+ # §12.7: it lapsed while the phone was showing it. 409, and the card
1971
+ # refreshes to the real status instead of silently minting a grant.
1972
+ return jsonify({"error": f"request is {row.get('status')}, not pending",
1973
+ "request": _override_row(row)}), 409
1974
+
1975
+ project_path = str(row.get("project_path") or ".")
1976
+ policy = opol.resolve(project_path)
1977
+ rule_class = str(row.get("rule_class") or "")
1978
+ confirm = data.get("confirm")
1979
+
1980
+ if decision == orq.DECISION_APPROVE:
1981
+ # (a) approving an access_deny / access_builtin request — the challenge
1982
+ # is the RULE GLOB itself, typed by hand (§8, §11 threat 1).
1983
+ if rule_class in opol.TYPED_CONFIRM_LAYERS and confirm != row.get("rule"):
1984
+ return jsonify({
1985
+ "error": f"approving an {rule_class} request needs the rule "
1986
+ "glob retyped by hand",
1987
+ "needs_confirmation": True,
1988
+ "confirm_with": row.get("rule"),
1989
+ }), 400
1990
+ # (b) a session grant, and only where policy allows one at all.
1991
+ if mode == orq.MODE_SESSION:
1992
+ if not policy.allow_session_grants:
1993
+ return jsonify({
1994
+ "error": "session grants are disabled for this project",
1995
+ "detail": "`override.allow_session_grants` is false — "
1996
+ "approve once instead.",
1997
+ }), 403
1998
+ if (rule_class not in opol.TYPED_CONFIRM_LAYERS
1999
+ and confirm != orq.CONFIRM_SESSION):
2000
+ return jsonify({
2001
+ "error": "a session grant is unlimited-use until it expires",
2002
+ "needs_confirmation": True,
2003
+ "confirm_with": orq.CONFIRM_SESSION,
2004
+ }), 400
2005
+
2006
+ requested_ttl = data.get("ttl_s")
2007
+ try:
2008
+ requested_ttl = int(requested_ttl) if requested_ttl is not None else None
2009
+ except (TypeError, ValueError):
2010
+ return jsonify({"error": "ttl_s must be an integer"}), 400
2011
+ requested_uses = data.get("uses")
2012
+ try:
2013
+ requested_uses = int(requested_uses) if requested_uses is not None else None
2014
+ except (TypeError, ValueError):
2015
+ return jsonify({"error": "uses must be an integer"}), 400
2016
+
2017
+ try:
2018
+ result = orq.decide(
2019
+ str(request_id), decision, uses=requested_uses,
2020
+ ttl_s=requested_ttl, note=str(data.get("note") or ""),
2021
+ decided_by=_DECIDED_BY, confirm=confirm, mode=mode,
2022
+ mute=bool(data.get("mute")))
2023
+ except orq.OverrideError as exc:
2024
+ _override_audit(f"{decision}_refused", row)
2025
+ return jsonify({"error": str(exc)}), 400
2026
+ except Exception as exc:
2027
+ return _svc_error(exc)
2028
+
2029
+ _override_audit(decision, result,
2030
+ confirmed=rule_class in opol.TYPED_CONFIRM_LAYERS,
2031
+ detail_extra={"mode": mode,
2032
+ "grant_id": result.get("grant_id", "")})
2033
+ _gw_audit(f"mobile.override.{decision}",
2034
+ {"request_id": str(request_id), "rule_class": rule_class,
2035
+ "mode": mode})
2036
+
2037
+ payload = {"request": _override_row(result), "decision": decision}
2038
+ if decision == orq.DECISION_APPROVE:
2039
+ granted_ttl = policy.clamp_ttl(requested_ttl)
2040
+ payload["grant"] = {
2041
+ "id": result.get("grant_id", ""),
2042
+ "mode": result.get("grant_mode", mode),
2043
+ "ttl_s": granted_ttl,
2044
+ "uses": policy.clamp_uses(
2045
+ opol.HARD_MAX_USES if mode == orq.MODE_SESSION
2046
+ else requested_uses),
2047
+ }
2048
+ # §8: a client asking for a week gets 15 minutes and is TOLD so.
2049
+ if requested_ttl is not None and granted_ttl < requested_ttl:
2050
+ payload["clamped"] = True
2051
+ payload["clamped_note"] = (
2052
+ f"requested ttl_s={requested_ttl} was clamped to "
2053
+ f"{granted_ttl}s by this project's override.max_ttl_s "
2054
+ f"({policy.max_ttl_s}s).")
2055
+ return jsonify(payload)
2056
+
2057
+
2058
+ @bp.route("/overrides/<request_id>/mute", methods=["POST"])
2059
+ def mobile_override_mute(request_id):
2060
+ """Deny, and suppress identical requests for this session (§8).
2061
+
2062
+ Never needs a confirmation: muting only ever makes the agent MORE blocked,
2063
+ and deny must always be the cheaper gesture than approve (§9).
2064
+ """
2065
+ gate = _override_write_gate()
2066
+ if gate:
2067
+ return gate
2068
+ from services import override_requests as orq
2069
+ data = request.get_json(silent=True) or {}
2070
+ row = orq.get(str(request_id))
2071
+ if row is None or _resolve_registered(str(row.get("project_path") or "")) is None:
2072
+ return jsonify({"error": "unknown request"}), 404
2073
+ if row.get("status") != orq.STATUS_PENDING:
2074
+ return jsonify({"error": f"request is {row.get('status')}, not pending",
2075
+ "request": _override_row(row)}), 409
2076
+ try:
2077
+ result = orq.decide(str(request_id), orq.DECISION_DENY,
2078
+ note=str(data.get("note") or ""),
2079
+ decided_by=_DECIDED_BY, mute=True)
2080
+ except orq.OverrideError as exc:
2081
+ return jsonify({"error": str(exc)}), 400
2082
+ except Exception as exc:
2083
+ return _svc_error(exc)
2084
+ _override_audit("mute", result, detail_extra={"muted": True})
2085
+ _gw_audit("mobile.override.mute", {"request_id": str(request_id)})
2086
+ return jsonify({"request": _override_row(result), "decision": "deny",
2087
+ "muted": True})
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-context-control"
7
- version = "2.70.0"
7
+ version = "2.71.0"
8
8
  description = "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."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"