code-context-control 2.67.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.67.0/code_context_control.egg-info → code_context_control-2.71.0}/PKG-INFO +3 -1
  2. {code_context_control-2.67.0 → code_context_control-2.71.0}/README.md +2 -0
  3. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/c3.py +296 -1
  4. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/commands/parser.py +102 -0
  5. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_access_guard.py +20 -0
  6. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_pretool_enforce.py +22 -0
  7. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_server.py +8 -18
  8. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/mcp_server.py +24 -0
  9. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/delegate.py +78 -35
  10. code_context_control-2.71.0/cli/tools/override.py +173 -0
  11. {code_context_control-2.67.0 → code_context_control-2.71.0/code_context_control.egg-info}/PKG-INFO +3 -1
  12. {code_context_control-2.67.0 → code_context_control-2.71.0}/code_context_control.egg-info/SOURCES.txt +13 -0
  13. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/config.py +30 -0
  14. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/oracle_server.py +11 -0
  15. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/oracle_ui.html +19 -0
  16. code_context_control-2.71.0/oracle/services/mobile_api.py +2087 -0
  17. code_context_control-2.71.0/oracle/ui/qrcode.js +2297 -0
  18. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/settings.js +43 -0
  19. {code_context_control-2.67.0 → code_context_control-2.71.0}/pyproject.toml +1 -1
  20. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/access_guard.py +32 -1
  21. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/credential_store.py +37 -0
  22. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/embedding_index.py +79 -10
  23. code_context_control-2.71.0/services/override_grants.py +511 -0
  24. code_context_control-2.71.0/services/override_policy.py +391 -0
  25. code_context_control-2.71.0/services/override_requests.py +572 -0
  26. code_context_control-2.71.0/tests/test_delegate_version_probe.py +247 -0
  27. code_context_control-2.71.0/tests/test_embedding_index_deadlock.py +321 -0
  28. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_enforcement_policy.py +17 -4
  29. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_enforcement_routes.py +10 -0
  30. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_enforcement_routes.py +9 -0
  31. code_context_control-2.71.0/tests/test_mobile_api.py +370 -0
  32. code_context_control-2.71.0/tests/test_mobile_override_routes.py +747 -0
  33. code_context_control-2.71.0/tests/test_mobile_security_api.py +719 -0
  34. code_context_control-2.71.0/tests/test_override_grants.py +442 -0
  35. code_context_control-2.71.0/tests/test_override_requests.py +329 -0
  36. {code_context_control-2.67.0 → code_context_control-2.71.0}/LICENSE +0 -0
  37. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/__init__.py +0 -0
  38. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/_hook_utils.py +0 -0
  39. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/commands/__init__.py +0 -0
  40. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/commands/common.py +0 -0
  41. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/docs.html +0 -0
  42. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/edits.html +0 -0
  43. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/access.html +0 -0
  44. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/bitbucket.html +0 -0
  45. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/credentials.html +0 -0
  46. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/getting-started.html +0 -0
  47. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/index.html +0 -0
  48. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/masking.html +0 -0
  49. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/oracle.html +0 -0
  50. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/shared.css +0 -0
  51. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/tools.html +0 -0
  52. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/guide/workflow.html +0 -0
  53. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_artifact.py +0 -0
  54. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_auto_snapshot.py +0 -0
  55. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_c3_signal.py +0 -0
  56. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_c3read.py +0 -0
  57. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_dispatch.py +0 -0
  58. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_edit_ledger.py +0 -0
  59. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_edit_unlock.py +0 -0
  60. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_filter.py +0 -0
  61. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_ghost_files.py +0 -0
  62. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_prompt_recall.py +0 -0
  63. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_read.py +0 -0
  64. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_session_stats.py +0 -0
  65. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hook_terse_advisor.py +0 -0
  66. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/app.js +0 -0
  67. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/add_project.js +0 -0
  68. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/config_editor.js +0 -0
  69. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  70. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_health.js +0 -0
  71. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_panel.js +0 -0
  72. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  73. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  74. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_views.js +0 -0
  75. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/global_search.js +0 -0
  76. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  77. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  78. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_locks.js +0 -0
  79. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  80. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/modals.js +0 -0
  81. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_card.js +0 -0
  82. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_tree.js +0 -0
  83. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/session_drawer.js +0 -0
  84. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/settings_modal.js +0 -0
  85. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/sidebar.js +0 -0
  86. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/summary_bar.js +0 -0
  87. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/task_board.js +0 -0
  88. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/toasts.js +0 -0
  89. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/components/topbar.js +0 -0
  90. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui/state.js +0 -0
  91. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/hub_ui.html +0 -0
  92. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/mcp_proxy.py +0 -0
  93. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/progress.py +0 -0
  94. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/server.py +0 -0
  95. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/__init__.py +0 -0
  96. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/_helpers.py +0 -0
  97. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/agent.py +0 -0
  98. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/artifacts.py +0 -0
  99. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/bitbucket.py +0 -0
  100. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/compress.py +0 -0
  101. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/credentials.py +0 -0
  102. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/edit.py +0 -0
  103. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/edits.py +0 -0
  104. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/federate.py +0 -0
  105. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/filter.py +0 -0
  106. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/impact.py +0 -0
  107. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/jira.py +0 -0
  108. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/locks.py +0 -0
  109. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/memory.py +0 -0
  110. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/project.py +0 -0
  111. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/read.py +0 -0
  112. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/search.py +0 -0
  113. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/session.py +0 -0
  114. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/shell.py +0 -0
  115. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/status.py +0 -0
  116. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/tasks.py +0 -0
  117. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/tools/validate.py +0 -0
  118. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/api.js +0 -0
  119. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/app.js +0 -0
  120. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/access.js +0 -0
  121. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/bitbucket.js +0 -0
  122. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/chat.js +0 -0
  123. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/credentials.js +0 -0
  124. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/dashboard.js +0 -0
  125. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/edits.js +0 -0
  126. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/enforcement.js +0 -0
  127. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/instructions.js +0 -0
  128. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/jira.js +0 -0
  129. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/memory.js +0 -0
  130. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/sessions.js +0 -0
  131. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/settings.js +0 -0
  132. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/sidebar.js +0 -0
  133. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/components/tasks.js +0 -0
  134. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/icons.js +0 -0
  135. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/pm_shared.js +0 -0
  136. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/shared.js +0 -0
  137. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui/theme.js +0 -0
  138. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui.html +0 -0
  139. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui_legacy.html +0 -0
  140. {code_context_control-2.67.0 → code_context_control-2.71.0}/cli/ui_nano.html +0 -0
  141. {code_context_control-2.67.0 → code_context_control-2.71.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  142. {code_context_control-2.67.0 → code_context_control-2.71.0}/code_context_control.egg-info/entry_points.txt +0 -0
  143. {code_context_control-2.67.0 → code_context_control-2.71.0}/code_context_control.egg-info/requires.txt +0 -0
  144. {code_context_control-2.67.0 → code_context_control-2.71.0}/code_context_control.egg-info/top_level.txt +0 -0
  145. {code_context_control-2.67.0 → code_context_control-2.71.0}/core/__init__.py +0 -0
  146. {code_context_control-2.67.0 → code_context_control-2.71.0}/core/config.py +0 -0
  147. {code_context_control-2.67.0 → code_context_control-2.71.0}/core/ide.py +0 -0
  148. {code_context_control-2.67.0 → code_context_control-2.71.0}/core/mcp_toml.py +0 -0
  149. {code_context_control-2.67.0 → code_context_control-2.71.0}/core/web_security.py +0 -0
  150. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/__init__.py +0 -0
  151. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/mcp_oracle.py +0 -0
  152. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/__init__.py +0 -0
  153. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/activity_reporter.py +0 -0
  154. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/api_auth.py +0 -0
  155. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/c3_bridge.py +0 -0
  156. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/chat_engine.py +0 -0
  157. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/chat_store.py +0 -0
  158. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/cross_memory.py +0 -0
  159. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/discovery_audit.py +0 -0
  160. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/federated_graph.py +0 -0
  161. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/health_checker.py +0 -0
  162. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/insight_engine.py +0 -0
  163. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/local_session.py +0 -0
  164. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/memory_reader.py +0 -0
  165. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/memory_writer.py +0 -0
  166. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/project_scanner.py +0 -0
  167. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/review_agent.py +0 -0
  168. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/tool_executor.py +0 -0
  169. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/services/tool_registry.py +0 -0
  170. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/activity.js +0 -0
  171. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/agents.js +0 -0
  172. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/app.js +0 -0
  173. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/busy.js +0 -0
  174. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/conversations.js +0 -0
  175. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/input.js +0 -0
  176. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/markdown.js +0 -0
  177. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/send.js +0 -0
  178. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/stream_renderer.js +0 -0
  179. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/chat/toolbar.js +0 -0
  180. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/core.js +0 -0
  181. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/crossgraph.js +0 -0
  182. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/header.js +0 -0
  183. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/insights.js +0 -0
  184. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/projects.js +0 -0
  185. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/suggestions.js +0 -0
  186. {code_context_control-2.67.0 → code_context_control-2.71.0}/oracle/ui/theme_tabs.js +0 -0
  187. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/__init__.py +0 -0
  188. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/access_telemetry.py +0 -0
  189. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/activity_log.py +0 -0
  190. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/agent_base.py +0 -0
  191. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/agent_locks.py +0 -0
  192. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/agents.py +0 -0
  193. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/artifact_defs.py +0 -0
  194. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/artifact_store.py +0 -0
  195. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/auto_memory.py +0 -0
  196. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bench/__init__.py +0 -0
  197. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bench/external/__init__.py +0 -0
  198. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bench/external/aider_polyglot.py +0 -0
  199. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bench/external/swe_bench.py +0 -0
  200. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/benchmark_dashboard.py +0 -0
  201. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bitbucket_client.py +0 -0
  202. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/bitbucket_credentials.py +0 -0
  203. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/circuit_breaker.py +0 -0
  204. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/claude_md.py +0 -0
  205. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/compressor.py +0 -0
  206. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/context_snapshot.py +0 -0
  207. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/conversation_store.py +0 -0
  208. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/doc_index.py +0 -0
  209. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/e2e_benchmark.py +0 -0
  210. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/e2e_evaluator.py +0 -0
  211. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/e2e_tasks.py +0 -0
  212. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/edit_ledger.py +0 -0
  213. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/enforcement_policy.py +0 -0
  214. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/error_reporting.py +0 -0
  215. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/file_memory.py +0 -0
  216. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/git_context.py +0 -0
  217. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/hub_service.py +0 -0
  218. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/indexer.py +0 -0
  219. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/jira_client.py +0 -0
  220. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/jira_cloud.py +0 -0
  221. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/jira_credentials.py +0 -0
  222. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/jira_data_center.py +0 -0
  223. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/jira_links.py +0 -0
  224. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/mask_activation.py +0 -0
  225. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/mask_mirror.py +0 -0
  226. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/mask_presets.py +0 -0
  227. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory.py +0 -0
  228. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_consolidator.py +0 -0
  229. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_distiller.py +0 -0
  230. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_graph.py +0 -0
  231. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_grounder.py +0 -0
  232. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_queue.py +0 -0
  233. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/memory_scorer.py +0 -0
  234. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/metrics.py +0 -0
  235. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/notifications.py +0 -0
  236. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/ollama_bridge.py +0 -0
  237. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/ollama_client.py +0 -0
  238. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/ollama_credentials.py +0 -0
  239. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/output_filter.py +0 -0
  240. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/parser.py +0 -0
  241. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/project_manager.py +0 -0
  242. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/project_runtime.py +0 -0
  243. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/protocol.py +0 -0
  244. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/proxy_state.py +0 -0
  245. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/repo_map.py +0 -0
  246. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/retention.py +0 -0
  247. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/retrieval_broker.py +0 -0
  248. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/router.py +0 -0
  249. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/runtime.py +0 -0
  250. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/scanner.py +0 -0
  251. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/session_benchmark.py +0 -0
  252. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/session_manager.py +0 -0
  253. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/session_preloader.py +0 -0
  254. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/subprojects.py +0 -0
  255. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/task_store.py +0 -0
  256. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/telemetry.py +0 -0
  257. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/text_index.py +0 -0
  258. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/time_tracker.py +0 -0
  259. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/tool_classifier.py +0 -0
  260. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/transcript_index.py +0 -0
  261. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/validation_cache.py +0 -0
  262. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/vector_store.py +0 -0
  263. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/watcher.py +0 -0
  264. {code_context_control-2.67.0 → code_context_control-2.71.0}/services/win_subprocess.py +0 -0
  265. {code_context_control-2.67.0 → code_context_control-2.71.0}/setup.cfg +0 -0
  266. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_builtin_optout.py +0 -0
  267. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_guard.py +0 -0
  268. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_guard_meta.py +0 -0
  269. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_guard_shell_project.py +0 -0
  270. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_guard_surfaces.py +0 -0
  271. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_access_guard_wiring.py +0 -0
  272. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_activity_reporter.py +0 -0
  273. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_agent_locks.py +0 -0
  274. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_aider_polyglot.py +0 -0
  275. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_antigravity_transition.py +0 -0
  276. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_artifact_store.py +0 -0
  277. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_artifact_tool.py +0 -0
  278. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  279. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_bitbucket_client.py +0 -0
  280. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_bitbucket_config_fallback.py +0 -0
  281. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_bitbucket_credentials.py +0 -0
  282. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_bitbucket_tool.py +0 -0
  283. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  284. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_c3_shell.py +0 -0
  285. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_circuit_breaker.py +0 -0
  286. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_claude_md_merge.py +0 -0
  287. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_cli_smoke.py +0 -0
  288. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_compressor_large_file.py +0 -0
  289. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_credential_store.py +0 -0
  290. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_credentials_routes.py +0 -0
  291. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_credentials_tool.py +0 -0
  292. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_creds_cli_smoke.py +0 -0
  293. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_delegate_cascade.py +0 -0
  294. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_e2e_benchmark.py +0 -0
  295. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_edit_ledger_hook.py +0 -0
  296. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_edit_locking.py +0 -0
  297. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_edit_normalization.py +0 -0
  298. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_enforcement_flip.py +0 -0
  299. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_federated_graph.py +0 -0
  300. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_filter_backoff.py +0 -0
  301. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_ghost_files.py +0 -0
  302. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_ghost_generation.py +0 -0
  303. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_git_branch_awareness.py +0 -0
  304. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_access_guard.py +0 -0
  305. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_dispatch.py +0 -0
  306. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_pretool_enforce.py +0 -0
  307. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_prompt_recall.py +0 -0
  308. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_smoke.py +0 -0
  309. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_state.py +0 -0
  310. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hook_utf8_stdio.py +0 -0
  311. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_credentials_route.py +0 -0
  312. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_credentials_routes_write.py +0 -0
  313. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_inspect_api.py +0 -0
  314. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_locks_routes.py +0 -0
  315. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_server_smoke.py +0 -0
  316. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_hub_subproject_links.py +0 -0
  317. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_ide_workflow_adaptation.py +0 -0
  318. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_init_scan.py +0 -0
  319. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_install_mcp_entrypoint.py +0 -0
  320. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_cli_smoke.py +0 -0
  321. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_client.py +0 -0
  322. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_config_fallback.py +0 -0
  323. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_credentials.py +0 -0
  324. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_links.py +0 -0
  325. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_routes.py +0 -0
  326. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_jira_tool.py +0 -0
  327. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_lazy_store_init.py +0 -0
  328. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mask_guard.py +0 -0
  329. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mask_routes.py +0 -0
  330. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mask_surfaces.py +0 -0
  331. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mcp_host_guard.py +0 -0
  332. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mcp_memory_timeout.py +0 -0
  333. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mcp_server_smoke.py +0 -0
  334. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_mcp_toml.py +0 -0
  335. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_memory_distiller.py +0 -0
  336. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_memory_graph_api.py +0 -0
  337. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_memory_queue.py +0 -0
  338. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_memory_system.py +0 -0
  339. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_no_stale_model_pins.py +0 -0
  340. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_notification_dedup.py +0 -0
  341. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_notification_discipline.py +0 -0
  342. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_ollama_credentials.py +0 -0
  343. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_api_auth.py +0 -0
  344. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_apikey_api.py +0 -0
  345. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_c3_bridge.py +0 -0
  346. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_chat_engine.py +0 -0
  347. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_chat_store.py +0 -0
  348. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_api.py +0 -0
  349. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_audit.py +0 -0
  350. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_local_auth.py +0 -0
  351. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_ollama_bridge.py +0 -0
  352. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_scanner_cache.py +0 -0
  353. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_security_fixes.py +0 -0
  354. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_session_bootstrap.py +0 -0
  355. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_subproject_awareness.py +0 -0
  356. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_oracle_ui_bundle.py +0 -0
  357. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_output_filter.py +0 -0
  358. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_permissions.py +0 -0
  359. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_pm_api.py +0 -0
  360. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_project_manager.py +0 -0
  361. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_project_manager_merge.py +0 -0
  362. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_project_tool.py +0 -0
  363. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_read_coercion.py +0 -0
  364. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_read_edit_parity.py +0 -0
  365. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_related_facts.py +0 -0
  366. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_repo_map.py +0 -0
  367. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_response_boilerplate.py +0 -0
  368. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_retention.py +0 -0
  369. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_review_digest.py +0 -0
  370. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_service_durability.py +0 -0
  371. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_session_benchmark.py +0 -0
  372. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_session_budget.py +0 -0
  373. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_shell_creds.py +0 -0
  374. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_shell_robustness.py +0 -0
  375. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_subproject_exclusion.py +0 -0
  376. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_subproject_federation.py +0 -0
  377. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_subprojects.py +0 -0
  378. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_swe_bench.py +0 -0
  379. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_task_store.py +0 -0
  380. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_task_tool.py +0 -0
  381. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_time_tracker.py +0 -0
  382. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_token_telemetry.py +0 -0
  383. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_tool_registry.py +0 -0
  384. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_upgrade_and_version.py +0 -0
  385. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_validate.py +0 -0
  386. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_vault_write_guard.py +0 -0
  387. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_version_sync.py +0 -0
  388. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_web_security.py +0 -0
  389. {code_context_control-2.67.0 → code_context_control-2.71.0}/tests/test_windows_reliability.py +0 -0
  390. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/__init__.py +0 -0
  391. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/backend.py +0 -0
  392. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/main.py +0 -0
  393. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/__init__.py +0 -0
  394. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/benchmark_view.py +0 -0
  395. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/claudemd_view.py +0 -0
  396. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/compress_view.py +0 -0
  397. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/index_view.py +0 -0
  398. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/init_view.py +0 -0
  399. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/mcp_view.py +0 -0
  400. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/optimize_view.py +0 -0
  401. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/pipe_view.py +0 -0
  402. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/projects_view.py +0 -0
  403. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/search_view.py +0 -0
  404. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/session_view.py +0 -0
  405. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/stats.py +0 -0
  406. {code_context_control-2.67.0 → code_context_control-2.71.0}/tui/screens/ui_view.py +0 -0
  407. {code_context_control-2.67.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.67.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
@@ -369,6 +369,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
369
369
  ## Security & privacy
370
370
 
371
371
  - **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.)*
372
+ - **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.
373
+ - **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.
372
374
  - **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.
373
375
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
374
376
  - **API keys** for third-party providers are read from the environment and never persisted by C3.
@@ -306,6 +306,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
306
306
  ## Security & privacy
307
307
 
308
308
  - **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.)*
309
+ - **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.
310
+ - **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.
309
311
  - **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.
310
312
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
311
313
  - **API keys** for third-party providers are read from the environment and never persisted by C3.
@@ -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.67.0"
95
+ __version__ = "2.71.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -261,6 +261,7 @@ _C3_MCP_ALLOW = [
261
261
  "mcp__c3__c3_bitbucket",
262
262
  "mcp__c3__c3_jira", "mcp__c3__c3_credentials",
263
263
  "mcp__c3__c3_project", "mcp__c3__c3_task", "mcp__c3__c3_artifacts",
264
+ "mcp__c3__c3_override",
264
265
  ]
265
266
 
266
267
  # Obsolete MCP tool names from earlier C3 versions. `c3 permissions clean`
@@ -6209,6 +6210,299 @@ def _enforce_audit(result: dict, project_path: str) -> None:
6209
6210
  pass
6210
6211
 
6211
6212
 
6213
+ _OVERRIDE_DEFAULT_TOOL = {"read": "Read", "write": "Edit"}
6214
+
6215
+
6216
+ def cmd_override(args):
6217
+ """`c3 override` — Override Requests: allow one blocked call, once.
6218
+
6219
+ The human half of docs/override-requests.md. Every verb here mints or
6220
+ inspects a *grant*; none of them edits policy. That separation is the
6221
+ entire point of the feature: today the only way to unblock an agent is to
6222
+ weaken a rule permanently, and a guard that spends its life in `advisory`
6223
+ is not a guard.
6224
+
6225
+ No agent surface reaches this command, and no approval here can touch the
6226
+ 'never' rows of spec §2 — the credential vault, Tier-0 absolute denies, or
6227
+ the catastrophic shell blocks.
6228
+ """
6229
+ sub = getattr(args, "override_cmd", None)
6230
+ if not sub:
6231
+ print("Usage: c3 override <policy|grant|list|revoke|check|sweep> ...")
6232
+ print(" c3 override policy # what is escalatable right now")
6233
+ return
6234
+
6235
+ from services import override_grants as og
6236
+ from services import override_policy as opol
6237
+
6238
+ project_path = str(Path(getattr(args, "project_path", ".") or ".").resolve())
6239
+ policy = opol.resolve(project_path)
6240
+ for warn in policy.warnings:
6241
+ print(f" [warn] {warn}")
6242
+
6243
+ if sub == "policy":
6244
+ _override_show(policy, project_path, opol)
6245
+ elif sub == "grant":
6246
+ _override_grant(args, policy, project_path, og, opol)
6247
+ elif sub == "list":
6248
+ _override_list(args, project_path, og)
6249
+ elif sub == "check":
6250
+ _override_check(args, policy, project_path, og, opol)
6251
+ elif sub == "revoke":
6252
+ gid = getattr(args, "grant_id", "")
6253
+ print(" Revoked." if og.revoke(project_path, gid)
6254
+ else f" No live grant with id '{gid}'.")
6255
+ elif sub == "sweep":
6256
+ print(f" Dropped {og.sweep_expired(project_path)} expired/spent grant(s).")
6257
+ elif sub == "requests":
6258
+ _override_requests(args, project_path)
6259
+ elif sub in ("approve", "deny"):
6260
+ _override_decide(args, sub, project_path)
6261
+
6262
+
6263
+ def _override_requests(args, project_path: str) -> None:
6264
+ from services import override_requests as orq
6265
+
6266
+ rows = orq.list_requests(
6267
+ project_path="" if getattr(args, "all_projects", False) else project_path,
6268
+ status=getattr(args, "status", "") or "")
6269
+ print_header("Override requests")
6270
+ if not rows:
6271
+ print(" (none)")
6272
+ return
6273
+ for r in rows:
6274
+ note = f" note: {r['decision_note']}" if r.get("decision_note") else ""
6275
+ print(f" {r['id']} {r['status']:<9} {r['tool']} {r['op']} {r['path']}")
6276
+ print(f" {'':18s} rule {r['rule']} ({r['rule_class']}) · session "
6277
+ f"{r['session_id']} · expires {r['expires_at']}{note}")
6278
+ if r.get("justification"):
6279
+ print(f" {'':18s} the agent wrote: \"{r['justification']}\"")
6280
+ print(f" {'':18s} (untrusted — it may be repeating text it read "
6281
+ f"from a file)")
6282
+ print("\n Approve: c3 override approve <id> Refuse: c3 override deny <id>")
6283
+
6284
+
6285
+ def _override_decide(args, decision: str, project_path: str) -> None:
6286
+ from services import override_requests as orq
6287
+
6288
+ try:
6289
+ row = orq.decide(getattr(args, "request_id", ""), decision,
6290
+ uses=getattr(args, "uses", None),
6291
+ ttl_s=getattr(args, "ttl_s", None),
6292
+ note=getattr(args, "note", "") or "",
6293
+ decided_by="cli",
6294
+ confirm=getattr(args, "confirm", None))
6295
+ except orq.OverrideError as exc:
6296
+ print(f" {exc}")
6297
+ return
6298
+ except ValueError as exc:
6299
+ print(f" Error: {exc}")
6300
+ return
6301
+
6302
+ if decision == "deny":
6303
+ print(f" Denied {row['id']}. The block stands.")
6304
+ return
6305
+ print_header("Override approved")
6306
+ print(f" Request : {row['id']} Grant: {row.get('grant_id', '?')}")
6307
+ print(f" Allows : {row['tool']} {row['op']} on {row['path']}")
6308
+ print(f" Session : {row['session_id']}")
6309
+ print(f"\n The rule {row['rule']} is still in force. One retry of this "
6310
+ "exact call, in")
6311
+ print(" that session, on that exact path — nothing else.")
6312
+
6313
+
6314
+ def _override_show(policy, project_path: str, opol) -> None:
6315
+ print_header("Override Requests")
6316
+ state = "on" if policy.enabled else "off"
6317
+ print(f" Enabled : {state} (channel: {policy.channel})")
6318
+ print(f" Project : {project_path}")
6319
+ print("\n Escalatable layers:")
6320
+ for key in opol.LAYER_KEYS:
6321
+ on = policy.escalatable(key)
6322
+ mark = "*" if on else " "
6323
+ extra = " (approval needs the rule typed by hand)" \
6324
+ if key in opol.TYPED_CONFIRM_LAYERS else ""
6325
+ print(f" {mark} {key:18s} {'yes' if on else 'no':3s}{extra}")
6326
+ print("\n Limits:")
6327
+ print(f" grant TTL : <= {policy.max_ttl_s}s (hard ceiling "
6328
+ f"{opol.HARD_MAX_TTL_S}s)")
6329
+ print(f" uses : {policy.default_uses} "
6330
+ f"(session grants: {'allowed' if policy.allow_session_grants else 'off'})")
6331
+ print(f" request TTL : {policy.request_ttl_s}s")
6332
+ print(f" rate : {policy.max_pending_per_session} pending/session, "
6333
+ f"{policy.max_requests_per_hour}/hour")
6334
+ if policy.corrupt_scopes:
6335
+ print(f"\n FAILING CLOSED — corrupt scope(s): "
6336
+ f"{', '.join(policy.corrupt_scopes)}")
6337
+ print("\n Never escalatable, at any setting: the credential vault, "
6338
+ ".c3/secrets.enc,")
6339
+ print(" .c3/cred_state.json, the dispatcher fail-closed deny, and the "
6340
+ "catastrophic")
6341
+ print(" shell blocks. Policy lives in .c3/config.json 'override' "
6342
+ "(human-edited).")
6343
+
6344
+
6345
+ def _override_identify(args, project_path: str, opol):
6346
+ """(gate, layer_key, rule, tool, op, error) for the call being approved.
6347
+
6348
+ Derived from the denial the path ACTUALLY produces, not from what the
6349
+ caller claims: a grant whose rule string does not match the rule that
6350
+ denies would never match at consume time, and a silently-useless grant is
6351
+ worse than a refusal.
6352
+ """
6353
+ op = getattr(args, "op", "read") or "read"
6354
+ tool = getattr(args, "tool", None) or _OVERRIDE_DEFAULT_TOOL[op]
6355
+ layer = getattr(args, "layer", None)
6356
+ target = getattr(args, "target", "")
6357
+
6358
+ if layer == "discipline":
6359
+ return (opol.GATE_DISCIPLINE, opol.LAYER_DISCIPLINE,
6360
+ opol.RULE_DISCIPLINE, tool, "write", None)
6361
+ if layer == "shell":
6362
+ return (opol.GATE_SHELL, opol.LAYER_SHELL_WARN,
6363
+ opol.RULE_SHELL_WARN, tool, op, None)
6364
+
6365
+ from services import access_guard as ag
6366
+ denial = ag.check(target, op, project_path)
6367
+ if denial is None:
6368
+ return (None, None, None, tool, op,
6369
+ f"`{target}` is not blocked for {op} — nothing to approve.")
6370
+ layer_key = opol.rule_class_for_denial(denial)
6371
+ if layer_key is None:
6372
+ return (None, None, None, tool, op,
6373
+ f"{opol.TAG_NOT_ESCALATABLE} `{denial.rule}` can never be "
6374
+ f"overridden — no config value and no approval reaches it.")
6375
+ gate = opol.GATE_FOR_LAYER_KEY[layer_key]
6376
+ if layer and layer != gate:
6377
+ return (None, None, None, tool, op,
6378
+ f"--layer {layer} does not match the layer that denied "
6379
+ f"({gate}, rule `{denial.rule}`).")
6380
+ return gate, layer_key, denial.rule, tool, op, None
6381
+
6382
+
6383
+ def _override_grant(args, policy, project_path: str, og, opol) -> None:
6384
+ gate, layer_key, rule, tool, op, err = _override_identify(
6385
+ args, project_path, opol)
6386
+ if err:
6387
+ print(f" {err}")
6388
+ return
6389
+ if not policy.enabled:
6390
+ print(" Overrides are disabled for this project.")
6391
+ print(" Enable by hand in .c3/config.json — the 'override' section, "
6392
+ "\"enabled\": true,")
6393
+ print(f" plus \"layers\": {{\"{layer_key}\": true}}. Nothing is "
6394
+ "escalatable by default.")
6395
+ return
6396
+ if not policy.escalatable(layer_key):
6397
+ print(f" The '{layer_key}' layer is not escalatable "
6398
+ f"(override.layers.{layer_key} is false).")
6399
+ print(" Opt in per layer, deliberately — that is the point of the "
6400
+ "default.")
6401
+ return
6402
+ if (layer_key in opol.TYPED_CONFIRM_LAYERS
6403
+ and getattr(args, "confirm", None) != rule):
6404
+ print(f" This approves ONE call against an {layer_key} rule "
6405
+ f"({rule}).")
6406
+ print(f" Retype the rule to confirm: --confirm '{rule}'")
6407
+ return
6408
+ try:
6409
+ grant = og.mint(project_path, session_id=getattr(args, "session_id", ""),
6410
+ layer=gate, rule=rule, tool=tool, op=op,
6411
+ path=getattr(args, "target", ""),
6412
+ ttl_s=getattr(args, "ttl_s", None),
6413
+ uses=getattr(args, "uses", None),
6414
+ granted_by="cli", policy=policy)
6415
+ except ValueError as exc:
6416
+ print(f" Error: {exc}")
6417
+ return
6418
+
6419
+ print_header("Override granted")
6420
+ print(f" Id : {grant['id']}")
6421
+ print(f" Allows : {tool} {op} on {grant['path_key']}")
6422
+ print(f" Layer : {gate} Rule: {rule}")
6423
+ print(f" Session : {grant['session_id']}")
6424
+ print(f" Expires : {grant['expires_at']} Uses: "
6425
+ f"{grant['uses_remaining']}")
6426
+ if getattr(args, "ttl_s", None) and grant.get("expires_at"):
6427
+ print(f" (TTL clamped to override.max_ttl_s = {policy.max_ttl_s}s "
6428
+ f"where it exceeded it)")
6429
+ print(f"\n The rule {rule} is still in force. This grant authorises one "
6430
+ "retry of")
6431
+ print(" this exact call, in this session, on this exact path, and "
6432
+ "nothing else.")
6433
+ _override_audit(grant, project_path)
6434
+
6435
+
6436
+ def _override_list(args, project_path: str, og) -> None:
6437
+ grants = og.active(project_path, getattr(args, "session_id", "") or "")
6438
+ print_header("Live override grants")
6439
+ if not grants:
6440
+ print(" (none)")
6441
+ for g in grants:
6442
+ print(f" {g.get('id', '?'):18s} {g.get('tool', '?')} "
6443
+ f"{g.get('op', '?')} {g.get('path_key', '?')}")
6444
+ print(f" {'':18s} rule {g.get('rule', '?')} · session "
6445
+ f"{g.get('session_id', '?')} · expires {g.get('expires_at', '?')}"
6446
+ f" · {g.get('uses_remaining', 0)} use(s) left")
6447
+ tail = int(getattr(args, "audit", 0) or 0)
6448
+ if tail:
6449
+ print("\n Audit (.c3/overrides.jsonl):")
6450
+ for line in og.read_audit(project_path, tail):
6451
+ print(f" {line.get('ts', '?')} {line.get('event', '?'):12s} "
6452
+ f"{line.get('grant_id', '')} {line.get('path', '')}")
6453
+
6454
+
6455
+ def _override_check(args, policy, project_path: str, og, opol) -> None:
6456
+ gate, layer_key, rule, tool, op, err = _override_identify(
6457
+ args, project_path, opol)
6458
+ if err:
6459
+ print(f" {err}")
6460
+ return
6461
+ if not policy.escalatable(layer_key):
6462
+ print(f" Denied — the '{layer_key}' layer is not escalatable, so no "
6463
+ "grant is even consulted.")
6464
+ return
6465
+ hit = og.find(project_path, session_id=getattr(args, "session_id", ""),
6466
+ layer=gate, rule=rule, tool=tool, op=op,
6467
+ path=getattr(args, "target", ""))
6468
+ if hit:
6469
+ print(f" Allowed by {hit['id']} — {hit['uses_remaining']} use(s) "
6470
+ f"left, expires {hit['expires_at']}.")
6471
+ print(" (`check` never consumes a use.)")
6472
+ else:
6473
+ print(" Denied — no live grant matches this exact "
6474
+ "session/layer/rule/tool/op/path.")
6475
+
6476
+
6477
+ def _override_audit(grant: dict, project_path: str) -> None:
6478
+ """Ledger + activity entry. The JSONL audit is written by the service."""
6479
+ try:
6480
+ from services.activity_log import ActivityLog
6481
+ ActivityLog(project_path).log("access_action", {
6482
+ "kind": "override", "action": "grant", "grant_id": grant["id"],
6483
+ "layer": grant["layer"], "rule": grant["rule"],
6484
+ "tool": grant["tool"], "op": grant["op"],
6485
+ "path": grant["path_key"], "session_id": grant["session_id"],
6486
+ "expires_at": grant["expires_at"],
6487
+ "uses": grant["uses_remaining"], "granted_by": grant["granted_by"],
6488
+ })
6489
+ except Exception:
6490
+ pass
6491
+ try:
6492
+ from services.edit_ledger import EditLedger
6493
+ EditLedger(project_path).log_edit(
6494
+ file=f"override://{grant['id']}",
6495
+ change_type="override_grant",
6496
+ summary=(f"granted {grant['tool']} {grant['op']} on "
6497
+ f"{grant['path_key']} once (rule {grant['rule']}, "
6498
+ f"expires {grant['expires_at']}) via `c3 override`"),
6499
+ tags=["override", "access"],
6500
+ detail={"kind": "override", **grant},
6501
+ )
6502
+ except Exception:
6503
+ pass
6504
+
6505
+
6212
6506
  def cmd_access(args):
6213
6507
  """Access Guard rule management — human-only mutation surface (spec §1)."""
6214
6508
  sub = getattr(args, "access_cmd", None)
@@ -7994,6 +8288,7 @@ def main():
7994
8288
  "creds": cmd_creds,
7995
8289
  "access": cmd_access,
7996
8290
  "enforce": cmd_enforce,
8291
+ "override": cmd_override,
7997
8292
  "locks": cmd_locks,
7998
8293
  "oracle": cmd_oracle,
7999
8294
  "upgrade": cmd_upgrade,
@@ -472,6 +472,108 @@ def build_parser(version: str, parse_cli_ide_arg):
472
472
  lk_sweep = locks_subs.add_parser("sweep", help="Drop expired leases now")
473
473
  lk_sweep.add_argument("--path", dest="project_path", default=".")
474
474
 
475
+ # ── Override Requests (v2.69.0, docs/override-requests.md) ──────────
476
+ # Human-only approval surface. A grant is single-use, session-bound,
477
+ # path-exact and TTL-capped, and it never edits policy: the rule that
478
+ # denied the call is still in force the moment the grant is spent.
479
+ # There is no agent-facing path to any verb in this command.
480
+ p_override = subparsers.add_parser(
481
+ "override",
482
+ help="Override Requests — allow one blocked call once, without weakening the rule",
483
+ )
484
+ override_subs = p_override.add_subparsers(dest="override_cmd")
485
+
486
+ ov_policy = override_subs.add_parser(
487
+ "policy", help="Show the effective `override` policy and which layers are escalatable"
488
+ )
489
+ ov_policy.add_argument("--path", dest="project_path", default=".",
490
+ help="Project directory (default: current)")
491
+
492
+ ov_grant = override_subs.add_parser(
493
+ "grant", help="Mint a single-use grant for one blocked call (human approval)"
494
+ )
495
+ ov_grant.add_argument("target", help="The exact path that was blocked")
496
+ ov_grant.add_argument("--session", dest="session_id", required=True,
497
+ help="Claude Code session id the grant is bound to — a grant never crosses sessions")
498
+ ov_grant.add_argument("--op", choices=["read", "write"], default="read",
499
+ help="Operation to allow (default: read)")
500
+ ov_grant.add_argument("--tool", default=None,
501
+ help="Exact tool name (default: Read for --op read, Edit for --op write)")
502
+ ov_grant.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
503
+ default=None,
504
+ help="Force a layer. Default: derive it from the denial the path actually produces")
505
+ ov_grant.add_argument("--ttl", type=int, default=None, dest="ttl_s",
506
+ help="Seconds until the grant expires (clamped to override.max_ttl_s, hard ceiling 900)")
507
+ ov_grant.add_argument("--uses", type=int, default=None,
508
+ help="Uses allowed (default 1; >1 needs override.allow_session_grants)")
509
+ ov_grant.add_argument("--confirm", default=None,
510
+ help="Required for deny/builtin rules: retype the rule glob by hand")
511
+ ov_grant.add_argument("--path", dest="project_path", default=".",
512
+ help="Project directory (default: current)")
513
+
514
+ ov_list = override_subs.add_parser("list", help="Live grants for this project")
515
+ ov_list.add_argument("--session", dest="session_id", default="",
516
+ help="Only grants bound to this session id")
517
+ ov_list.add_argument("--audit", type=int, default=0, metavar="N",
518
+ help="Also print the last N lines of .c3/overrides.jsonl")
519
+ ov_list.add_argument("--path", dest="project_path", default=".",
520
+ help="Project directory (default: current)")
521
+
522
+ ov_revoke = override_subs.add_parser("revoke", help="Drop a live grant before it is used")
523
+ ov_revoke.add_argument("grant_id", help="Grant id (grt_…)")
524
+ ov_revoke.add_argument("--path", dest="project_path", default=".",
525
+ help="Project directory (default: current)")
526
+
527
+ ov_check = override_subs.add_parser(
528
+ "check", help="Would a grant cover this call right now? (never consumes one)"
529
+ )
530
+ ov_check.add_argument("target", help="Path to test")
531
+ ov_check.add_argument("--session", dest="session_id", required=True)
532
+ ov_check.add_argument("--op", choices=["read", "write"], default="read")
533
+ ov_check.add_argument("--tool", default=None)
534
+ ov_check.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
535
+ default=None)
536
+ ov_check.add_argument("--path", dest="project_path", default=".",
537
+ help="Project directory (default: current)")
538
+
539
+ ov_sweep = override_subs.add_parser("sweep", help="Drop expired / spent grants now")
540
+ ov_sweep.add_argument("--path", dest="project_path", default=".",
541
+ help="Project directory (default: current)")
542
+
543
+ # Requests: what agents have ASKED for. Deciding is human-only and lives
544
+ # here (and, later, on the phone) — never on the c3_override agent tool.
545
+ ov_requests = override_subs.add_parser(
546
+ "requests", help="Override requests agents have asked for"
547
+ )
548
+ ov_requests.add_argument("--status", default="",
549
+ help="pending | approved | denied | expired | withdrawn")
550
+ ov_requests.add_argument("--all", dest="all_projects", action="store_true",
551
+ help="Every project, not just this one")
552
+ ov_requests.add_argument("--path", dest="project_path", default=".",
553
+ help="Project directory (default: current)")
554
+
555
+ ov_approve = override_subs.add_parser(
556
+ "approve", help="Approve one request — mints a single-use grant"
557
+ )
558
+ ov_approve.add_argument("request_id", help="Request id (ovr_…)")
559
+ ov_approve.add_argument("--ttl", type=int, default=None, dest="ttl_s",
560
+ help="Seconds until the grant expires (clamped to override.max_ttl_s)")
561
+ ov_approve.add_argument("--uses", type=int, default=None,
562
+ help="Uses allowed (default 1)")
563
+ ov_approve.add_argument("--note", default="",
564
+ help="Note recorded with the decision")
565
+ ov_approve.add_argument("--confirm", default=None,
566
+ help="Required for deny/builtin rules: retype the rule glob by hand")
567
+ ov_approve.add_argument("--path", dest="project_path", default=".",
568
+ help="Project directory (default: current)")
569
+
570
+ ov_deny = override_subs.add_parser("deny", help="Refuse one request")
571
+ ov_deny.add_argument("request_id", help="Request id (ovr_…)")
572
+ ov_deny.add_argument("--note", default="",
573
+ help="Note the agent will see with the refusal")
574
+ ov_deny.add_argument("--path", dest="project_path", default=".",
575
+ help="Project directory (default: current)")
576
+
475
577
  # ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
476
578
  # LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
477
579
  # `c3 access` (path policy — a security boundary) and from
@@ -81,6 +81,22 @@ def _record(denial, tool: str, operation: str, path: str,
81
81
  pass
82
82
 
83
83
 
84
+ def _override_allows(base: str, denial, *, tool: str, op: str, path: str,
85
+ session_id: str):
86
+ """A live override grant covering this exact call, or None (spec §5).
87
+
88
+ Lazy import and policy-before-grants ordering keep the hot path free: a
89
+ project that never turned overrides on pays one dict lookup and never
90
+ opens the grants file. Any failure returns None — the denial stands.
91
+ """
92
+ try:
93
+ from services import override_grants as og # noqa: PLC0415 — lazy
94
+ return og.gate_access(base, denial, tool=tool, op=op, path=path,
95
+ session_id=session_id)
96
+ except Exception:
97
+ return None # fail closed: no grant, ordinary denial
98
+
99
+
84
100
  def _target(tool_input: dict) -> str:
85
101
  return str(
86
102
  tool_input.get("file_path")
@@ -136,6 +152,10 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
136
152
  op = "write" if tool in _WRITE_TOOLS else "read"
137
153
  denial = ag.check(fp, op, base)
138
154
  if denial:
155
+ granted = _override_allows(base, denial, tool=tool, op=op,
156
+ path=fp, session_id=session_id)
157
+ if granted:
158
+ return {"additionalContext": granted}
139
159
  _record(denial, tool, op, fp, base, session_id)
140
160
  return _deny(ag.refusal(denial, fp, op, surface="hook", tool=tool))
141
161
  return None
@@ -360,6 +360,25 @@ def _vault_denial(tool_name: str, tool_input: dict) -> dict | None:
360
360
  return None
361
361
 
362
362
 
363
+ def _override_allows(base: Path, tool_name: str, tool_input: dict,
364
+ session_id: str) -> str | None:
365
+ """A live discipline grant for this exact write, or None (spec §5).
366
+
367
+ Runs AFTER `_vault_denial`, which stays unconditional: no grant, no
368
+ config value and no approval can open a native write path to the vault.
369
+ """
370
+ target = str(tool_input.get("file_path")
371
+ or tool_input.get("notebook_path") or "")
372
+ if not target:
373
+ return None
374
+ try:
375
+ from services import override_grants as og # noqa: PLC0415 — lazy
376
+ return og.gate_discipline(base, tool=tool_name, path=target,
377
+ session_id=session_id)
378
+ except Exception:
379
+ return None # fail closed: no grant, ordinary block
380
+
381
+
363
382
  def run(payload: dict, project_path: Path | None = None) -> dict | None:
364
383
  """Core enforcement logic — importable by the dispatcher and tests.
365
384
 
@@ -421,6 +440,9 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
421
440
  # the ledger stays complete either way; what is lost is the pre-edit
422
441
  # snapshot c3_edit would have taken.
423
442
  if tool_name in blocked_tools and mode == "strict":
443
+ granted = _override_allows(base, tool_name, tool_input, session_id)
444
+ if granted:
445
+ return _with_warnings({"additionalContext": granted}, policy)
424
446
  _record_block(tool_name, tool_input, session_id, base)
425
447
  reason = (
426
448
  f"[c3:enforce] Native `{tool_name}` is blocked to preserve the edit "
@@ -1780,25 +1780,15 @@ def api_projects_config_get():
1780
1780
  # returned by any hub route (no reveal exists here). `credentials` stays out of
1781
1781
  # _CONFIG_WRITE_SECTIONS — these dedicated routes are the only hub write path.
1782
1782
 
1783
- _CRED_PUBLIC_FIELDS = ("scope", "type", "value_len", "env_var", "inject",
1784
- "agent_readable", "description", "storage", "created",
1785
- "updated")
1786
-
1787
-
1788
1783
  def _cred_entry_public(name, entry, *, usage=None, shadows_global=None):
1789
- """Explicit allowlist serializer — structurally cannot emit a value."""
1790
- rec = {"name": name}
1791
- for key in _CRED_PUBLIC_FIELDS:
1792
- rec[key] = entry.get(key, "")
1793
- rec["value_len"] = entry.get("value_len", 0)
1794
- rec["inject"] = bool(entry.get("inject"))
1795
- rec["agent_readable"] = bool(entry.get("agent_readable"))
1796
- if usage is not None:
1797
- rec["last_used"] = (usage.get(name) or {}).get("last_used", "")
1798
- rec["use_count"] = (usage.get(name) or {}).get("use_count", 0)
1799
- if shadows_global is not None:
1800
- rec["shadows_global"] = bool(shadows_global)
1801
- return rec
1784
+ """Explicit allowlist serializer — structurally cannot emit a value.
1785
+
1786
+ Delegates to credential_store.public_entry so the hub, the per-project UI
1787
+ and the mobile gateway all serialize through one allowlist; a second copy
1788
+ is exactly the drift the write-only wire contract cannot survive."""
1789
+ from services import credential_store as cred_store
1790
+ return cred_store.public_entry(
1791
+ name, entry, usage=usage, shadows_global=shadows_global)
1802
1792
 
1803
1793
 
1804
1794
  def _resolve_cred_target(path: str, scope: str, *, mutation: bool):
@@ -758,6 +758,30 @@ async def c3_locks(action: str = "list", paths: str = "", intent: str = "",
758
758
  svc, finalize)
759
759
 
760
760
 
761
+ @mcp.tool()
762
+ async def c3_override(action: str = "list", path: str = "", tool: str = "",
763
+ op: str = "read", why: str = "", request_id: str = "",
764
+ layer: str = "", timeout_s: int = 60,
765
+ ctx: Context = None) -> str:
766
+ """ASK A HUMAN to allow ONE blocked call — never a policy change.
767
+ actions: request, status, wait (blocks <=180s), list, withdraw.
768
+ request: path = the exact blocked path; why = one concrete sentence for the human.
769
+ A yes mints a single-use, session-bound, path-exact grant with a short TTL; the
770
+ rule that blocked you stays in force. There is NO approve action here — only the
771
+ user can decide, from their phone or `c3 override approve`.
772
+ Most denials are NOT escalatable (credential vault, Tier-0, catastrophic shell
773
+ commands); those are refused here and never shown to anyone. If a refusal did not
774
+ invite you to ask, do not ask — mark the step blocked and tell the user."""
775
+ svc = _svc(ctx)
776
+
777
+ def finalize(name, args, resp, summ, **kw):
778
+ return _finalize_response(ctx, name, args, resp, summ, **kw)
779
+
780
+ from cli.tools.override import handle_override
781
+ return await asyncio.to_thread(handle_override, action, path, tool, op, why,
782
+ request_id, layer, timeout_s, svc, finalize)
783
+
784
+
761
785
  @mcp.tool()
762
786
  async def c3_impact(target: str, file_path: str = "", mode: str = "symbol",
763
787
  ctx: Context = None) -> str: