code-context-control 2.115.0__tar.gz → 2.118.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. {code_context_control-2.115.0 → code_context_control-2.118.1}/PKG-INFO +1 -1
  2. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/c3.py +134 -33
  3. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/commands/parser.py +21 -4
  4. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/credentials.html +31 -10
  5. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_server.py +79 -7
  6. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_access.js +140 -5
  7. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_credentials.js +13 -5
  8. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/mcp_server.py +11 -5
  9. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/server.py +13 -9
  10. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/access.js +51 -7
  11. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/credentials.js +13 -5
  12. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/PKG-INFO +1 -1
  13. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/mobile_api.py +57 -7
  14. {code_context_control-2.115.0 → code_context_control-2.118.1}/pyproject.toml +1 -1
  15. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/access_guard.py +198 -73
  16. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/claude_md.py +1 -1
  17. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/credential_store.py +155 -36
  18. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/override_grants.py +224 -35
  19. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/override_policy.py +84 -1
  20. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/override_requests.py +113 -21
  21. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/session_benchmark.py +31 -8
  22. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/shell_output.py +57 -3
  23. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_builtin_optout.py +17 -4
  24. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_guard_meta.py +7 -1
  25. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_builtin_modes.py +91 -5
  26. code_context_control-2.118.1/tests/test_credential_login_kind.py +365 -0
  27. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_credential_ui_parity.py +40 -4
  28. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_override_routes.py +116 -0
  29. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_override_grants.py +168 -0
  30. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_override_requests.py +89 -0
  31. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_output.py +98 -0
  32. code_context_control-2.115.0/tests/test_credential_login_kind.py +0 -191
  33. {code_context_control-2.115.0 → code_context_control-2.118.1}/LICENSE +0 -0
  34. {code_context_control-2.115.0 → code_context_control-2.118.1}/README.md +0 -0
  35. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/__init__.py +0 -0
  36. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/_hook_utils.py +0 -0
  37. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/_shell_writes.py +0 -0
  38. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/commands/__init__.py +0 -0
  39. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/commands/common.py +0 -0
  40. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/docs.html +0 -0
  41. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/edits.html +0 -0
  42. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/access.html +0 -0
  43. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/bitbucket.html +0 -0
  44. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/getting-started.html +0 -0
  45. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/index.html +0 -0
  46. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/masking.html +0 -0
  47. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/oracle.html +0 -0
  48. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/shared.css +0 -0
  49. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/tools.html +0 -0
  50. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/guide/workflow.html +0 -0
  51. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_access_guard.py +0 -0
  52. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_artifact.py +0 -0
  53. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_auto_snapshot.py +0 -0
  54. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_c3_signal.py +0 -0
  55. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_c3read.py +0 -0
  56. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_dispatch.py +0 -0
  57. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_edit_ledger.py +0 -0
  58. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_edit_unlock.py +0 -0
  59. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_filter.py +0 -0
  60. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_ghost_files.py +0 -0
  61. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_pretool_enforce.py +0 -0
  62. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_prompt_recall.py +0 -0
  63. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_read.py +0 -0
  64. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_session_stats.py +0 -0
  65. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hook_terse_advisor.py +0 -0
  66. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/app.js +0 -0
  67. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/add_project.js +0 -0
  68. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/config_editor.js +0 -0
  69. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
  70. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_health.js +0 -0
  71. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_panel.js +0 -0
  72. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_subprojects.js +0 -0
  73. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_tasks.js +0 -0
  74. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/drill_views.js +0 -0
  75. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/global_search.js +0 -0
  76. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_ci.js +0 -0
  77. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  78. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_enforcement.js +0 -0
  79. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_locks.js +0 -0
  80. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/hub_tokens.js +0 -0
  81. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/mcp_manager.js +0 -0
  82. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/modals.js +0 -0
  83. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/project_card.js +0 -0
  84. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/project_tree.js +0 -0
  85. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/session_drawer.js +0 -0
  86. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/settings_modal.js +0 -0
  87. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/sidebar.js +0 -0
  88. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/summary_bar.js +0 -0
  89. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/task_board.js +0 -0
  90. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/toasts.js +0 -0
  91. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/components/topbar.js +0 -0
  92. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui/state.js +0 -0
  93. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/hub_ui.html +0 -0
  94. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/mcp_proxy.py +0 -0
  95. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/progress.py +0 -0
  96. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/__init__.py +0 -0
  97. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/_grants.py +0 -0
  98. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/_helpers.py +0 -0
  99. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/agent.py +0 -0
  100. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/artifacts.py +0 -0
  101. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/bitbucket.py +0 -0
  102. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/ci.py +0 -0
  103. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/compress.py +0 -0
  104. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/credentials.py +0 -0
  105. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/delegate.py +0 -0
  106. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/edit.py +0 -0
  107. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/edit_verify.py +0 -0
  108. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/edits.py +0 -0
  109. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/federate.py +0 -0
  110. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/filter.py +0 -0
  111. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/impact.py +0 -0
  112. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/jira.py +0 -0
  113. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/locks.py +0 -0
  114. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/memory.py +0 -0
  115. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/override.py +0 -0
  116. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/project.py +0 -0
  117. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/read.py +0 -0
  118. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/search.py +0 -0
  119. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/session.py +0 -0
  120. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/shell.py +0 -0
  121. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/shell_job.py +0 -0
  122. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/shell_nudge.py +0 -0
  123. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/shell_parsers.py +0 -0
  124. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/shell_render.py +0 -0
  125. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/status.py +0 -0
  126. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/tasks.py +0 -0
  127. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/tools/validate.py +0 -0
  128. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/api.js +0 -0
  129. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/app.js +0 -0
  130. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/bitbucket.js +0 -0
  131. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/chat.js +0 -0
  132. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/dashboard.js +0 -0
  133. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/edits.js +0 -0
  134. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/enforcement.js +0 -0
  135. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/instructions.js +0 -0
  136. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/jira.js +0 -0
  137. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/memory.js +0 -0
  138. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/sessions.js +0 -0
  139. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/settings.js +0 -0
  140. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/sidebar.js +0 -0
  141. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/components/tasks.js +0 -0
  142. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/icons.js +0 -0
  143. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/pm_shared.js +0 -0
  144. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/shared.js +0 -0
  145. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui/theme.js +0 -0
  146. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui.html +0 -0
  147. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui_legacy.html +0 -0
  148. {code_context_control-2.115.0 → code_context_control-2.118.1}/cli/ui_nano.html +0 -0
  149. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/SOURCES.txt +0 -0
  150. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/dependency_links.txt +0 -0
  151. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/entry_points.txt +0 -0
  152. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/requires.txt +0 -0
  153. {code_context_control-2.115.0 → code_context_control-2.118.1}/code_context_control.egg-info/top_level.txt +0 -0
  154. {code_context_control-2.115.0 → code_context_control-2.118.1}/core/__init__.py +0 -0
  155. {code_context_control-2.115.0 → code_context_control-2.118.1}/core/config.py +0 -0
  156. {code_context_control-2.115.0 → code_context_control-2.118.1}/core/ide.py +0 -0
  157. {code_context_control-2.115.0 → code_context_control-2.118.1}/core/mcp_toml.py +0 -0
  158. {code_context_control-2.115.0 → code_context_control-2.118.1}/core/web_security.py +0 -0
  159. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/__init__.py +0 -0
  160. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/config.py +0 -0
  161. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/mcp_oracle.py +0 -0
  162. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/oracle_server.py +0 -0
  163. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/oracle_ui.html +0 -0
  164. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/__init__.py +0 -0
  165. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/activity_reporter.py +0 -0
  166. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/api_auth.py +0 -0
  167. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/c3_bridge.py +0 -0
  168. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/chat_engine.py +0 -0
  169. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/chat_poll.py +0 -0
  170. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/chat_store.py +0 -0
  171. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/cross_memory.py +0 -0
  172. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/discovery_audit.py +0 -0
  173. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/federated_graph.py +0 -0
  174. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/health_checker.py +0 -0
  175. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/insight_engine.py +0 -0
  176. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/local_session.py +0 -0
  177. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/memory_reader.py +0 -0
  178. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/memory_writer.py +0 -0
  179. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/project_scanner.py +0 -0
  180. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/review_agent.py +0 -0
  181. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/tool_executor.py +0 -0
  182. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/services/tool_registry.py +0 -0
  183. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/activity.js +0 -0
  184. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/agents.js +0 -0
  185. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/app.js +0 -0
  186. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/busy.js +0 -0
  187. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/conversations.js +0 -0
  188. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/input.js +0 -0
  189. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/markdown.js +0 -0
  190. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/send.js +0 -0
  191. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/stream_renderer.js +0 -0
  192. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/chat/toolbar.js +0 -0
  193. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/core.js +0 -0
  194. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/crossgraph.js +0 -0
  195. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/header.js +0 -0
  196. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/insights.js +0 -0
  197. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/projects.js +0 -0
  198. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/qrcode.js +0 -0
  199. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/settings.js +0 -0
  200. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/suggestions.js +0 -0
  201. {code_context_control-2.115.0 → code_context_control-2.118.1}/oracle/ui/theme_tabs.js +0 -0
  202. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/__init__.py +0 -0
  203. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/access_telemetry.py +0 -0
  204. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/activity_log.py +0 -0
  205. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/agent_base.py +0 -0
  206. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/agent_locks.py +0 -0
  207. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/agents.py +0 -0
  208. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/artifact_defs.py +0 -0
  209. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/artifact_store.py +0 -0
  210. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/auto_memory.py +0 -0
  211. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/background_service.py +0 -0
  212. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/__init__.py +0 -0
  213. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/external/__init__.py +0 -0
  214. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/external/aider_polyglot.py +0 -0
  215. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/external/swe_bench.py +0 -0
  216. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/search_eval.py +0 -0
  217. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bench/shell_eval.py +0 -0
  218. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/benchmark_dashboard.py +0 -0
  219. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bitbucket_client.py +0 -0
  220. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/bitbucket_credentials.py +0 -0
  221. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_act.py +0 -0
  222. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_cache.py +0 -0
  223. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_expr.py +0 -0
  224. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_failures.py +0 -0
  225. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_github.py +0 -0
  226. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_impact.py +0 -0
  227. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_intel.py +0 -0
  228. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_runner.py +0 -0
  229. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ci_workflow.py +0 -0
  230. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/circuit_breaker.py +0 -0
  231. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/compressor.py +0 -0
  232. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/context_snapshot.py +0 -0
  233. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/conversation_store.py +0 -0
  234. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/cred_audit.py +0 -0
  235. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/cred_telemetry.py +0 -0
  236. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/doc_index.py +0 -0
  237. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/e2e_benchmark.py +0 -0
  238. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/e2e_evaluator.py +0 -0
  239. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/e2e_tasks.py +0 -0
  240. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/edit_ledger.py +0 -0
  241. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/embedding_index.py +0 -0
  242. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/enforcement_policy.py +0 -0
  243. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/error_reporting.py +0 -0
  244. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/file_memory.py +0 -0
  245. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/git_context.py +0 -0
  246. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/hub_service.py +0 -0
  247. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/index_store.py +0 -0
  248. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/indexer.py +0 -0
  249. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/jira_client.py +0 -0
  250. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/jira_cloud.py +0 -0
  251. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/jira_credentials.py +0 -0
  252. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/jira_data_center.py +0 -0
  253. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/jira_links.py +0 -0
  254. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/lexical_index.py +0 -0
  255. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/mask_activation.py +0 -0
  256. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/mask_mirror.py +0 -0
  257. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/mask_presets.py +0 -0
  258. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory.py +0 -0
  259. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_consolidator.py +0 -0
  260. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_distiller.py +0 -0
  261. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_graph.py +0 -0
  262. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_grounder.py +0 -0
  263. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_queue.py +0 -0
  264. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/memory_scorer.py +0 -0
  265. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/metrics.py +0 -0
  266. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/notifications.py +0 -0
  267. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ollama_bridge.py +0 -0
  268. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ollama_client.py +0 -0
  269. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/ollama_credentials.py +0 -0
  270. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/oracle_service.py +0 -0
  271. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/output_filter.py +0 -0
  272. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/override_wake.py +0 -0
  273. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/parser.py +0 -0
  274. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/project_manager.py +0 -0
  275. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/project_runtime.py +0 -0
  276. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/protocol.py +0 -0
  277. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/proxy_state.py +0 -0
  278. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/repo_map.py +0 -0
  279. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/repo_shape.py +0 -0
  280. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/reranker.py +0 -0
  281. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/retention.py +0 -0
  282. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/retrieval.py +0 -0
  283. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/retrieval_broker.py +0 -0
  284. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/router.py +0 -0
  285. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/runtime.py +0 -0
  286. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/scanner.py +0 -0
  287. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/session_manager.py +0 -0
  288. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/session_preloader.py +0 -0
  289. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/shell_jobs.py +0 -0
  290. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/subprojects.py +0 -0
  291. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/task_store.py +0 -0
  292. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/telemetry.py +0 -0
  293. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/text_index.py +0 -0
  294. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/time_tracker.py +0 -0
  295. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/tool_classifier.py +0 -0
  296. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/transcript_index.py +0 -0
  297. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/validation_cache.py +0 -0
  298. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/vector_store.py +0 -0
  299. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/watcher.py +0 -0
  300. {code_context_control-2.115.0 → code_context_control-2.118.1}/services/win_subprocess.py +0 -0
  301. {code_context_control-2.115.0 → code_context_control-2.118.1}/setup.cfg +0 -0
  302. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_guard.py +0 -0
  303. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_guard_shell_project.py +0 -0
  304. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_guard_surfaces.py +0 -0
  305. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_access_guard_wiring.py +0 -0
  306. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_activity_reporter.py +0 -0
  307. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_agent_locks.py +0 -0
  308. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_aider_polyglot.py +0 -0
  309. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_antigravity_transition.py +0 -0
  310. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_artifact_store.py +0 -0
  311. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_artifact_tool.py +0 -0
  312. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_background_service.py +0 -0
  313. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_bitbucket_cli_smoke.py +0 -0
  314. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_bitbucket_client.py +0 -0
  315. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_bitbucket_config_fallback.py +0 -0
  316. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_bitbucket_credentials.py +0 -0
  317. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_bitbucket_tool.py +0 -0
  318. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_c3_bridge_project_artifacts.py +0 -0
  319. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_c3_shell.py +0 -0
  320. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_chat_poll.py +0 -0
  321. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_act.py +0 -0
  322. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_cache.py +0 -0
  323. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_expr.py +0 -0
  324. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_impact.py +0 -0
  325. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_intel.py +0 -0
  326. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_runner.py +0 -0
  327. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ci_workflow.py +0 -0
  328. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_circuit_breaker.py +0 -0
  329. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_claude_md_merge.py +0 -0
  330. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_claude_md_updater.py +0 -0
  331. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_cli_smoke.py +0 -0
  332. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_compressor_large_file.py +0 -0
  333. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_confirm_guard.py +0 -0
  334. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_confirm_guard_wiring.py +0 -0
  335. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_cooccurrence_bounds.py +0 -0
  336. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_cred_audit.py +0 -0
  337. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_cred_telemetry.py +0 -0
  338. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_credential_store.py +0 -0
  339. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_credentials_routes.py +0 -0
  340. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_credentials_tool.py +0 -0
  341. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_creds_cli_smoke.py +0 -0
  342. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_delegate_cascade.py +0 -0
  343. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_delegate_version_probe.py +0 -0
  344. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_e2e_benchmark.py +0 -0
  345. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_edit_ledger_deadline.py +0 -0
  346. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_edit_ledger_hook.py +0 -0
  347. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_edit_locking.py +0 -0
  348. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_edit_normalization.py +0 -0
  349. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_edit_verify.py +0 -0
  350. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_embedding_index_deadlock.py +0 -0
  351. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_enforcement_flip.py +0 -0
  352. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_enforcement_policy.py +0 -0
  353. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_enforcement_routes.py +0 -0
  354. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_env_parser.py +0 -0
  355. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_failed_calls_do_not_unlock.py +0 -0
  356. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_federated_graph.py +0 -0
  357. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_filter_backoff.py +0 -0
  358. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ghost_files.py +0 -0
  359. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ghost_generation.py +0 -0
  360. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_git_branch_awareness.py +0 -0
  361. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_access_guard.py +0 -0
  362. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_codex_compat.py +0 -0
  363. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_dispatch.py +0 -0
  364. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_pretool_enforce.py +0 -0
  365. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_prompt_recall.py +0 -0
  366. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_smoke.py +0 -0
  367. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_state.py +0 -0
  368. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hook_utf8_stdio.py +0 -0
  369. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_credentials_route.py +0 -0
  370. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_credentials_routes_write.py +0 -0
  371. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_enforcement_routes.py +0 -0
  372. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_inspect_api.py +0 -0
  373. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_locks_routes.py +0 -0
  374. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_oracle_service_routes.py +0 -0
  375. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_server_smoke.py +0 -0
  376. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_hub_subproject_links.py +0 -0
  377. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ide_workflow_adaptation.py +0 -0
  378. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_index_cap_not_hardcoded.py +0 -0
  379. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_init_clear_preserves_user_content.py +0 -0
  380. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_init_scan.py +0 -0
  381. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_install_mcp_entrypoint.py +0 -0
  382. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_cli_smoke.py +0 -0
  383. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_client.py +0 -0
  384. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_config_fallback.py +0 -0
  385. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_credentials.py +0 -0
  386. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_links.py +0 -0
  387. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_routes.py +0 -0
  388. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_jira_tool.py +0 -0
  389. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_lazy_store_init.py +0 -0
  390. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mask_guard.py +0 -0
  391. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mask_routes.py +0 -0
  392. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mask_surfaces.py +0 -0
  393. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mcp_host_guard.py +0 -0
  394. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mcp_memory_timeout.py +0 -0
  395. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mcp_server_smoke.py +0 -0
  396. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mcp_toml.py +0 -0
  397. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_memory_distiller.py +0 -0
  398. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_memory_graph_api.py +0 -0
  399. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_memory_queue.py +0 -0
  400. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_memory_system.py +0 -0
  401. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mobile_api.py +0 -0
  402. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mobile_extras.py +0 -0
  403. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mobile_override_routes.py +0 -0
  404. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_mobile_security_api.py +0 -0
  405. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_no_stale_model_pins.py +0 -0
  406. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_notification_dedup.py +0 -0
  407. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_notification_discipline.py +0 -0
  408. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ollama_credentials.py +0 -0
  409. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_api_auth.py +0 -0
  410. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_apikey_api.py +0 -0
  411. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_c3_bridge.py +0 -0
  412. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_chat_engine.py +0 -0
  413. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_chat_store.py +0 -0
  414. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_discovery_api.py +0 -0
  415. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_discovery_audit.py +0 -0
  416. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_health_probe.py +0 -0
  417. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_local_auth.py +0 -0
  418. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_ollama_bridge.py +0 -0
  419. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_scanner_cache.py +0 -0
  420. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_security_fixes.py +0 -0
  421. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_session_bootstrap.py +0 -0
  422. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_subproject_awareness.py +0 -0
  423. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_oracle_ui_bundle.py +0 -0
  424. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_output_filter.py +0 -0
  425. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_override_grants_mcp.py +0 -0
  426. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_override_wake.py +0 -0
  427. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_permissions.py +0 -0
  428. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_pm_api.py +0 -0
  429. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_project_manager.py +0 -0
  430. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_project_manager_merge.py +0 -0
  431. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_project_tool.py +0 -0
  432. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_read_coercion.py +0 -0
  433. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_read_edit_parity.py +0 -0
  434. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_related_facts.py +0 -0
  435. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_repo_map.py +0 -0
  436. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_repo_shape.py +0 -0
  437. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_response_boilerplate.py +0 -0
  438. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_retention.py +0 -0
  439. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_review_digest.py +0 -0
  440. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_eval.py +0 -0
  441. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p1.py +0 -0
  442. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p2.py +0 -0
  443. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p2b.py +0 -0
  444. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p3.py +0 -0
  445. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p4.py +0 -0
  446. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_search_p5.py +0 -0
  447. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_service_durability.py +0 -0
  448. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_session_benchmark.py +0 -0
  449. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_session_budget.py +0 -0
  450. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_budget_s1.py +0 -0
  451. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_creds.py +0 -0
  452. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_eval.py +0 -0
  453. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_jobs_s3.py +0 -0
  454. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_nudge_s4.py +0 -0
  455. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_parsers_s2.py +0 -0
  456. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_robustness.py +0 -0
  457. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_telemetry_s0.py +0 -0
  458. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_write_ledger.py +0 -0
  459. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_shell_writes.py +0 -0
  460. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subproject_clear_scope.py +0 -0
  461. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subproject_depth_consumers.py +0 -0
  462. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subproject_exclusion.py +0 -0
  463. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subproject_federation.py +0 -0
  464. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subproject_linkage_api.py +0 -0
  465. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_subprojects.py +0 -0
  466. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_swe_bench.py +0 -0
  467. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_task_store.py +0 -0
  468. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_task_tool.py +0 -0
  469. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_time_tracker.py +0 -0
  470. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_token_telemetry.py +0 -0
  471. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_token_tracking.py +0 -0
  472. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_tool_registry.py +0 -0
  473. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_ui_jsx_syntax.py +0 -0
  474. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_upgrade_and_version.py +0 -0
  475. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_validate.py +0 -0
  476. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_vault_write_guard.py +0 -0
  477. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_version_sync.py +0 -0
  478. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_web_security.py +0 -0
  479. {code_context_control-2.115.0 → code_context_control-2.118.1}/tests/test_windows_reliability.py +0 -0
  480. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/__init__.py +0 -0
  481. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/backend.py +0 -0
  482. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/main.py +0 -0
  483. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/__init__.py +0 -0
  484. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/benchmark_view.py +0 -0
  485. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/claudemd_view.py +0 -0
  486. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/compress_view.py +0 -0
  487. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/index_view.py +0 -0
  488. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/init_view.py +0 -0
  489. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/mcp_view.py +0 -0
  490. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/optimize_view.py +0 -0
  491. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/pipe_view.py +0 -0
  492. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/projects_view.py +0 -0
  493. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/search_view.py +0 -0
  494. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/session_view.py +0 -0
  495. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/stats.py +0 -0
  496. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/screens/ui_view.py +0 -0
  497. {code_context_control-2.115.0 → code_context_control-2.118.1}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.115.0
3
+ Version: 2.118.1
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.115.0"
95
+ __version__ = "2.118.1"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -6338,7 +6338,16 @@ def _creds_entry_line(name: str, entry: dict) -> str:
6338
6338
  return " ".join(parts)
6339
6339
 
6340
6340
 
6341
- _CREDS_HIDDEN_FIELDS = {"number", "cvc", "ssn"} # prompted via getpass
6341
+ #: Fields prompted with getpass instead of input(). This list was missing
6342
+ #: `password` and `totp_secret` from v2.90.0 to v2.118.0, so
6343
+ #: `c3 creds set X --type login` echoed the password to the terminal — both
6344
+ #: JS UIs masked it and nothing asserted the CLI. tests/test_creds_cli.py now
6345
+ #: derives the expected set from _SCHEMAS, so a future kind cannot reopen it.
6346
+ _CREDS_HIDDEN_FIELDS = {"number", "cvc", "ssn", "password", "private_key",
6347
+ "passphrase", "totp_secret"}
6348
+ #: A private key is pasted as many lines; getpass reads one. Read these until
6349
+ #: a blank line instead of silently storing the first line of a PEM.
6350
+ _CREDS_MULTILINE_FIELDS = {"private_key"}
6342
6351
 
6343
6352
 
6344
6353
  def _creds_prompt_structured(name: str, ctype: str) -> str:
@@ -6355,7 +6364,16 @@ def _creds_prompt_structured(name: str, ctype: str) -> str:
6355
6364
  fields: dict = {}
6356
6365
  for fname in list(required) + list(optional):
6357
6366
  tag = "" if fname in required else " (optional)"
6358
- if fname in _CREDS_HIDDEN_FIELDS:
6367
+ if fname in _CREDS_MULTILINE_FIELDS:
6368
+ print(f" {fname}{tag} — paste it, then a blank line:")
6369
+ lines = []
6370
+ while True:
6371
+ line = input()
6372
+ if not line.strip():
6373
+ break
6374
+ lines.append(line)
6375
+ raw = "\n".join(lines)
6376
+ elif fname in _CREDS_HIDDEN_FIELDS:
6359
6377
  raw = getpass.getpass(f" {fname}{tag}: ")
6360
6378
  else:
6361
6379
  raw = input(f" {fname}{tag}: ")
@@ -7031,12 +7049,14 @@ def _override_requests(args, project_path: str) -> None:
7031
7049
  def _override_decide(args, decision: str, project_path: str) -> None:
7032
7050
  from services import override_requests as orq
7033
7051
 
7052
+ mode = getattr(args, "mode", None) or orq.MODE_ONCE
7034
7053
  try:
7035
7054
  row = orq.decide(getattr(args, "request_id", ""), decision,
7036
7055
  uses=getattr(args, "uses", None),
7037
7056
  ttl_s=getattr(args, "ttl_s", None),
7038
7057
  note=getattr(args, "note", "") or "",
7039
- decided_by="cli",
7058
+ decided_by="cli", mode=mode,
7059
+ mute=bool(getattr(args, "mute", False)),
7040
7060
  confirm=getattr(args, "confirm", None))
7041
7061
  except orq.OverrideError as exc:
7042
7062
  print(f" {exc}")
@@ -7046,15 +7066,29 @@ def _override_decide(args, decision: str, project_path: str) -> None:
7046
7066
  return
7047
7067
 
7048
7068
  if decision == "deny":
7049
- print(f" Denied {row['id']}. The block stands.")
7069
+ muted = " This session will not be asked again." \
7070
+ if row.get("muted") else ""
7071
+ print(f" Denied {row['id']}. The block stands.{muted}")
7050
7072
  return
7051
7073
  print_header("Override approved")
7052
7074
  print(f" Request : {row['id']} Grant: {row.get('grant_id', '?')}")
7053
7075
  print(f" Allows : {row['tool']} {row['op']} on {row['path']}")
7054
7076
  print(f" Session : {row['session_id']}")
7055
- print(f"\n The rule {row['rule']} is still in force. One retry of this "
7056
- "exact call, in")
7057
- print(" that session, on that exact path — nothing else.")
7077
+ # The reach line is the whole point of printing anything here: a session
7078
+ # or rule grant is a standing capability, and saying "one retry" for one
7079
+ # would be a lie the user acts on.
7080
+ print(f"\n The rule {row['rule']} is still in force.")
7081
+ if mode == orq.MODE_RULE:
7082
+ print(" This grant covers EVERY path that rule matches, for any tool")
7083
+ print(" in the same op class, in that session — until it expires or")
7084
+ print(" the session stops using it. `c3 override list` shows it;")
7085
+ print(" `c3 override revoke <grant_id>` ends it.")
7086
+ elif mode == orq.MODE_SESSION:
7087
+ print(" Unlimited retries of this exact call, in that session, on")
7088
+ print(" that exact path, until the grant expires.")
7089
+ else:
7090
+ print(" One retry of this exact call, in that session, on that exact")
7091
+ print(" path — nothing else.")
7058
7092
 
7059
7093
 
7060
7094
  def _override_show(policy, project_path: str, opol) -> None:
@@ -7074,6 +7108,14 @@ def _override_show(policy, project_path: str, opol) -> None:
7074
7108
  f"{opol.HARD_MAX_TTL_S}s)")
7075
7109
  print(f" uses : {policy.default_uses} "
7076
7110
  f"(session grants: {'allowed' if policy.allow_session_grants else 'off'})")
7111
+ if policy.allow_rule_grants:
7112
+ print(f" rule grants : allowed — <= {policy.rule_grant_ttl_s}s "
7113
+ f"(hard ceiling {opol.HARD_MAX_RULE_TTL_S}s), idle cap "
7114
+ f"{policy.rule_grant_idle_s}s")
7115
+ print(" one approval covers every path the rule "
7116
+ "matches")
7117
+ else:
7118
+ print(" rule grants : off")
7077
7119
  print(f" request TTL : {policy.request_ttl_s}s")
7078
7120
  print(f" rate : {policy.max_pending_per_session} pending/session, "
7079
7121
  f"{policy.max_requests_per_hour}/hour")
@@ -7185,11 +7227,22 @@ def _override_list(args, project_path: str, og) -> None:
7185
7227
  if not grants:
7186
7228
  print(" (none)")
7187
7229
  for g in grants:
7188
- print(f" {g.get('id', '?'):18s} {g.get('tool', '?')} "
7189
- f"{g.get('op', '?')} {g.get('path_key', '?')}")
7230
+ wide = g.get("scope") == og.SCOPE_RULE
7231
+ left = ("unlimited use" if g.get("uses_remaining") is None
7232
+ else f"{g.get('uses_remaining', 0)} use(s) left")
7233
+ # A rule grant's path_key is where it was MINTED, not what it covers.
7234
+ # Printing it in the same column as a call grant's target would read
7235
+ # as a much smaller capability than it is.
7236
+ reach = (f"any path matching {g.get('rule', '?')}" if wide
7237
+ else g.get("path_key", "?"))
7238
+ print(f" {g.get('id', '?'):18s} [{'rule' if wide else 'call'}] "
7239
+ f"{g.get('tool', '?')} {g.get('op', '?')} {reach}")
7190
7240
  print(f" {'':18s} rule {g.get('rule', '?')} · session "
7191
7241
  f"{g.get('session_id', '?')} · expires {g.get('expires_at', '?')}"
7192
- f" · {g.get('uses_remaining', 0)} use(s) left")
7242
+ f" · {left}"
7243
+ + (f" · idle cap {g.get('idle_s')}s" if g.get("idle_s") else ""))
7244
+ if wide:
7245
+ print(f" {'':18s} minted from {g.get('path_key', '?')}")
7193
7246
  tail = int(getattr(args, "audit", 0) or 0)
7194
7247
  if tail:
7195
7248
  print("\n Audit (.c3/overrides.jsonl):")
@@ -7212,8 +7265,10 @@ def _override_check(args, policy, project_path: str, og, opol) -> None:
7212
7265
  layer=gate, rule=rule, tool=tool, op=op,
7213
7266
  path=getattr(args, "target", ""))
7214
7267
  if hit:
7215
- print(f" Allowed by {hit['id']} — {hit['uses_remaining']} use(s) "
7216
- f"left, expires {hit['expires_at']}.")
7268
+ left = ("unlimited use" if hit.get("uses_remaining") is None
7269
+ else f"{hit['uses_remaining']} use(s) left")
7270
+ print(f" Allowed by {hit['id']} ({hit.get('scope', 'call')} scope) "
7271
+ f"— {left}, expires {hit['expires_at']}.")
7217
7272
  print(" (`check` never consumes a use.)")
7218
7273
  else:
7219
7274
  print(" Denied — no live grant matches this exact "
@@ -7392,19 +7447,30 @@ def _access_cmd_list(args, project_path: str) -> None:
7392
7447
  print(" [warn] access section invalid — scope fails closed "
7393
7448
  "(deny-all); fix config.json 'access' by hand")
7394
7449
  if scope == "builtin":
7450
+ # Which SCOPE set each live mode. Printing a project-set opt-out
7451
+ # as if it were machine-wide (or the reverse) is the exact
7452
+ # confusion the realm split exists to prevent.
7453
+ realms = sec.get("mode_realms") or {}
7454
+
7455
+ def _where(glob):
7456
+ r = realms.get(access_guard._norm_builtin(glob), "")
7457
+ return f" [{r}]" if r else ""
7458
+
7395
7459
  off = sec.get("disabled") or []
7396
7460
  if off:
7397
7461
  print(" DISABLED by you (not enforced):")
7398
7462
  for glob in off:
7399
- print(f" {'off':<10} {glob}")
7400
- print(" re-enable: c3 access builtin enable <glob>")
7463
+ print(f" {'off':<10} {glob}{_where(glob)}")
7464
+ print(" re-enable: c3 access builtin enable <glob> "
7465
+ "[--project]")
7401
7466
  moded = {g: m for g, m in (sec.get("modes") or {}).items()
7402
7467
  if m not in ("default", "allow")}
7403
7468
  if moded:
7404
7469
  print(" MODE set by you (departs from the shipped default):")
7405
7470
  for glob, m in sorted(moded.items()):
7406
- print(f" {m:<10} {glob}")
7407
- print(" reset: c3 access builtin mode <glob> default")
7471
+ print(f" {m:<10} {glob}{_where(glob)}")
7472
+ print(" reset: c3 access builtin mode <glob> default "
7473
+ "[--project]")
7408
7474
  can = [g for g in (sec.get("disableable") or [])
7409
7475
  if access_guard._norm_builtin(g) not in set(off)
7410
7476
  and access_guard._norm_builtin(g) not in moded]
@@ -7414,6 +7480,8 @@ def _access_cmd_list(args, project_path: str) -> None:
7414
7480
  print(" disable: c3 access builtin disable <glob> · "
7415
7481
  "granular: c3 access builtin mode <glob> "
7416
7482
  "{deny,confirm,allow}")
7483
+ print(" add --project to scope it to this project only "
7484
+ "(attested under its own keyring realm)")
7417
7485
  if sec.get("absolute"):
7418
7486
  print(" absolute (credential vault, cannot be disabled): "
7419
7487
  + ", ".join(sec["absolute"]))
@@ -7430,7 +7498,8 @@ def _access_cmd_list(args, project_path: str) -> None:
7430
7498
 
7431
7499
 
7432
7500
  def _access_cmd_builtin(args, project_path: str) -> None:
7433
- """Switch a Tier-1 builtin off or back on. Human-only; always global scope.
7501
+ """Switch a Tier-1 builtin off or back on. Human-only; global scope by
7502
+ default, this project only with --project (v2.118.0).
7434
7503
 
7435
7504
  Disabling asks for typed confirmation unless --yes: these guards are what
7436
7505
  stop an agent rewriting its own hooks, settings or git history, and the
@@ -7452,8 +7521,11 @@ def _access_cmd_builtin(args, project_path: str) -> None:
7452
7521
  return
7453
7522
 
7454
7523
  disabling = action == "disable"
7524
+ scope = "project" if getattr(args, "project_scope", False) else "global"
7455
7525
  if disabling and not getattr(args, "yes", False):
7456
- print(f"About to STOP enforcing the builtin guard on: {glob}")
7526
+ where = (f"in THIS project only ({project_path})" if scope == "project"
7527
+ else "on EVERY project on this machine")
7528
+ print(f"About to STOP enforcing the builtin guard on: {glob} {where}")
7457
7529
  print(" While off, any agent with C3 tools can write those paths.")
7458
7530
  if glob.endswith("settings*.json") or ".claude" in glob:
7459
7531
  print(" This is the file that configures the agent itself — hooks,")
@@ -7464,23 +7536,29 @@ def _access_cmd_builtin(args, project_path: str) -> None:
7464
7536
  return
7465
7537
 
7466
7538
  try:
7467
- result = access_guard.set_builtin_disabled(glob, disabling)
7539
+ result = access_guard.set_builtin_disabled(glob, disabling, scope,
7540
+ project_path)
7468
7541
  except ValueError as exc:
7469
7542
  print(f"[error] {exc}")
7470
7543
  return
7471
7544
 
7472
- _access_audit(f"builtin_{action}", result["glob"], "builtin", "global",
7473
- project_path)
7545
+ _access_audit(f"builtin_{action}", result["glob"], "builtin",
7546
+ result["scope"], project_path)
7474
7547
  if not result["changed"]:
7475
7548
  state = "already disabled" if disabling else "already enforced"
7476
- print(f"[=] {result['glob']} — {state}")
7549
+ print(f"[=] {result['glob']} — {state} ({result['scope']} scope)")
7477
7550
  return
7551
+ flag = " --project" if result["scope"] == "project" else ""
7478
7552
  if disabling:
7479
- print(f"[OK] Builtin '{result['glob']}' DISABLED (global scope).")
7553
+ print(f"[OK] Builtin '{result['glob']}' DISABLED "
7554
+ f"({result['scope']} scope).")
7480
7555
  print(" Re-enable with: c3 access builtin enable "
7481
- f"{result['glob']}")
7556
+ f"{result['glob']}{flag}")
7557
+ if result["glob"].endswith(".env*"):
7558
+ print(_ENV_VAULT_HINT)
7482
7559
  else:
7483
- print(f"[OK] Builtin '{result['glob']}' re-enforced.")
7560
+ print(f"[OK] Builtin '{result['glob']}' re-enforced "
7561
+ f"({result['scope']} scope).")
7484
7562
  if not result["attested"]:
7485
7563
  print(" [warn] keyring attestation could not be cleared; the "
7486
7564
  "builtin is enforced anyway (config entry removed).")
@@ -7496,8 +7574,18 @@ _BUILTIN_MODE_HELP = {
7496
7574
  }
7497
7575
 
7498
7576
 
7577
+ #: Shown whenever someone loosens the `.env` guard. `.env` is the one Tier-1
7578
+ #: builtin whose whole content is secrets, and C3 already has a place to put
7579
+ #: them that the agent cannot read. Advisory — never a refusal.
7580
+ _ENV_VAULT_HINT = (
7581
+ " [note] .env values are better held in the vault: "
7582
+ "`c3_credentials import_env` reads the file server-side, so the agent\n"
7583
+ " gets names, lengths and fingerprints and never a value."
7584
+ )
7585
+
7586
+
7499
7587
  def _access_cmd_builtin_mode(args, glob: str, project_path: str) -> None:
7500
- """`c3 access builtin mode <glob> <mode>` — granular builtin control.
7588
+ """`c3 access builtin mode <glob> <mode> [--project]` — granular control.
7501
7589
 
7502
7590
  `deny` and `default` never prompt (tightening / reset). `confirm` and
7503
7591
  `allow` weaken the guard, so they get the same typed-glob beat of
@@ -7506,10 +7594,13 @@ def _access_cmd_builtin_mode(args, glob: str, project_path: str) -> None:
7506
7594
  from services import access_guard
7507
7595
 
7508
7596
  mode = getattr(args, "mode", None)
7597
+ scope = "project" if getattr(args, "project_scope", False) else "global"
7598
+ where = (f"in THIS project only ({project_path})" if scope == "project"
7599
+ else "on EVERY project on this machine")
7509
7600
  if mode in ("confirm", "allow") and not getattr(args, "yes", False):
7510
7601
  widening = ("STOP enforcing" if mode == "allow"
7511
7602
  else "PAUSE (not block) and ask you to approve")
7512
- print(f"About to make the builtin guard on {glob} {widening}.")
7603
+ print(f"About to make the builtin guard on {glob} {widening} {where}.")
7513
7604
  if mode == "allow":
7514
7605
  print(" While off, any agent with C3 tools can touch those paths.")
7515
7606
  else:
@@ -7524,20 +7615,30 @@ def _access_cmd_builtin_mode(args, glob: str, project_path: str) -> None:
7524
7615
  return
7525
7616
 
7526
7617
  try:
7527
- result = access_guard.set_builtin_mode(glob, mode)
7618
+ result = access_guard.set_builtin_mode(glob, mode, scope, project_path)
7528
7619
  except ValueError as exc:
7529
7620
  print(f"[error] {exc}")
7530
7621
  return
7531
7622
 
7532
7623
  _access_audit(f"builtin_mode_{result['mode']}", result["glob"],
7533
- "builtin", "global", project_path)
7624
+ "builtin", result["scope"], project_path)
7534
7625
  if not result["changed"]:
7535
- print(f"[=] {result['glob']} — already {result['mode']}")
7626
+ print(f"[=] {result['glob']} — already {result['mode']} "
7627
+ f"({result['scope']} scope)")
7536
7628
  return
7537
7629
  print(f"[OK] Builtin '{result['glob']}' mode = {result['mode']} "
7538
- f"(global scope): {_BUILTIN_MODE_HELP[result['mode']]}.")
7630
+ f"({result['scope']} scope): {_BUILTIN_MODE_HELP[result['mode']]}.")
7631
+ if result["scope"] == "project":
7632
+ print(" Bound to this project's keyring realm — a clone of this "
7633
+ "repo carries the config\n but not the attestation, so the "
7634
+ "builtin stays enforced there.")
7539
7635
  if result["mode"] != "default":
7540
- print(f" Reset with: c3 access builtin mode {result['glob']} default")
7636
+ flag = " --project" if result["scope"] == "project" else ""
7637
+ print(f" Reset with: c3 access builtin mode {result['glob']} "
7638
+ f"default{flag}")
7639
+ if result["glob"].endswith(".env*") and result["mode"] in ("allow",
7640
+ "confirm"):
7641
+ print(_ENV_VAULT_HINT)
7541
7642
  if not result["attested"]:
7542
7643
  print(" [warn] keyring attestation could not be cleared; the "
7543
7644
  "default is enforced anyway (config entry removed).")
@@ -735,10 +735,18 @@ def build_parser(version: str, parse_cli_ide_arg):
735
735
  help="Seconds until the grant expires (clamped to override.max_ttl_s)")
736
736
  ov_approve.add_argument("--uses", type=int, default=None,
737
737
  help="Uses allowed (default 1)")
738
+ ov_approve.add_argument(
739
+ "--mode", choices=("once", "session", "rule"), default="once",
740
+ help="once (default) | session: unlimited uses on THIS call until the "
741
+ "grant expires (needs override.allow_session_grants and "
742
+ "--confirm session) | rule: every path the rule matches, any "
743
+ "tool in the same op class (needs override.allow_rule_grants "
744
+ "and the rule glob in --confirm)")
738
745
  ov_approve.add_argument("--note", default="",
739
746
  help="Note recorded with the decision")
740
747
  ov_approve.add_argument("--confirm", default=None,
741
- help="Required for deny/builtin rules: retype the rule glob by hand")
748
+ help="Required for deny/builtin rules and for --mode rule: "
749
+ "retype the rule glob by hand ('session' for --mode session)")
742
750
  ov_approve.add_argument("--path", dest="project_path", default=".",
743
751
  help="Project directory (default: current)")
744
752
 
@@ -746,6 +754,8 @@ def build_parser(version: str, parse_cli_ide_arg):
746
754
  ov_deny.add_argument("request_id", help="Request id (ovr_…)")
747
755
  ov_deny.add_argument("--note", default="",
748
756
  help="Note the agent will see with the refusal")
757
+ ov_deny.add_argument("--mute", action="store_true",
758
+ help="Also stop this session asking the same thing again")
749
759
  ov_deny.add_argument("--path", dest="project_path", default=".",
750
760
  help="Project directory (default: current)")
751
761
 
@@ -830,10 +840,12 @@ def build_parser(version: str, parse_cli_ide_arg):
830
840
  # ── Builtin opt-out (two-key) ───────────────────────────────────────
831
841
  # Builtins are on by default. Switching one off needs a config entry AND
832
842
  # a keyring attestation, so an agent that writes config.json alone still
833
- # cannot loosen the guard. Global scope only — project scopes may only
834
- # ever tighten. The credential-vault builtins are never disableable.
843
+ # cannot loosen the guard. Since v2.118.0 the pair may be written at
844
+ # PROJECT scope too, because the attestation is realm-bound: a cloned
845
+ # repo carries the config half and changes nothing. The credential-vault
846
+ # builtins are never disableable at any scope.
835
847
  ac_builtin = access_subs.add_parser(
836
- "builtin", help="Disable or re-enable a built-in guard (global, needs confirmation)")
848
+ "builtin", help="Disable or re-enable a built-in guard (needs confirmation)")
837
849
  builtin_subs = ac_builtin.add_subparsers(dest="builtin_cmd")
838
850
  for _name, _help in (("disable", "Stop enforcing a built-in guard"),
839
851
  ("enable", "Re-enforce a built-in guard")):
@@ -841,6 +853,8 @@ def build_parser(version: str, parse_cli_ide_arg):
841
853
  _p.add_argument("glob", help="One of: **/.env*, **/.c3/**, **/.claude/settings*.json, **/.git/**")
842
854
  _p.add_argument("--path", dest="project_path", default=".",
843
855
  help="Project directory used for the audit log (default: current)")
856
+ _p.add_argument("--project", dest="project_scope", action="store_true",
857
+ help="This project only (default: every project on this machine)")
844
858
  if _name == "disable":
845
859
  _p.add_argument("--yes", action="store_true",
846
860
  help="Skip the typed confirmation (scripts/CI)")
@@ -857,6 +871,9 @@ def build_parser(version: str, parse_cli_ide_arg):
857
871
  "approval; allow = off (two-key); default = shipped behaviour")
858
872
  bm.add_argument("--path", dest="project_path", default=".",
859
873
  help="Project directory used for the audit log (default: current)")
874
+ bm.add_argument("--project", dest="project_scope", action="store_true",
875
+ help="Set the mode for THIS project only, attested under "
876
+ "its own keyring realm (default: every project)")
860
877
  bm.add_argument("--yes", action="store_true",
861
878
  help="Skip the typed confirmation (scripts/CI)")
862
879
 
@@ -308,8 +308,9 @@ c3_shell(cmd=<span class="str">'terraform apply'</span>, env_creds=<span class="
308
308
  <code>card</code> (cardholder, number, expiry, optional cvc/billing_zip — Luhn-checked),
309
309
  <code>address</code> (street1, city, state, zip, optional recipient/street2/country/phone),
310
310
  <code>identity</code> (full_name, optional dob/ssn/phone/email) and
311
- <code>login</code> (site_id, canonical_origin, username, password, optional totp_secret —
312
- v2.90.0). The payload is stored as one
311
+ <code>login</code> (site_id, canonical_target, username, optional
312
+ password/private_key/passphrase/totp_secret — v2.90.0, generalized v2.118.0).
313
+ The payload is stored as one
313
314
  canonical JSON object through the same keyring / encrypted-sidecar path as any other value.</p>
314
315
 
315
316
  <p>They are <strong>inject-only by construction</strong>, which is stricter than a normal secret:</p>
@@ -323,9 +324,10 @@ c3_shell(cmd=<span class="str">'terraform apply'</span>, env_creds=<span class="
323
324
  <li><strong>The registry shows a projection, not the data.</strong> Lists and the UI display
324
325
  <code>visa ••••4242</code> for a card (brand + last4, deliberately <em>not</em> expiry),
325
326
  city/state for an address, the name for an identity, and
326
- <code>github · https://github.com · 2FA</code> for a login — site, origin and a 2FA
327
- boolean, with the <em>username withheld on purpose</em>, because username + origin is
328
- half the credential. Field <em>names</em> are public;
327
+ <code>github · https://github.com · 2FA</code> or
328
+ <code>build01 · ssh://build01.lan:22 · key</code> for a login — site, target and
329
+ 2FA/key booleans, with the <em>username withheld on purpose</em>, because username +
330
+ target is half the credential. Field <em>names</em> are public;
329
331
  field <em>values</em> never transit any HTTP response — a sweep test enforces this.</li>
330
332
  <li><strong>Echoes redact per field</strong> to <code>[cred:NAME.field]</code>. Caveat: the
331
333
  redactor only tracks values of 4+ characters, so a 3-digit CVC or 2-letter state that a
@@ -346,15 +348,34 @@ c3_shell(cmd=<span class="str">'terraform apply'</span>, env_creds=<span class="
346
348
 
347
349
  <p><strong><code>login</code> is storage only, and that is a design constraint, not a
348
350
  gap.</strong> C3 has no browser surface and must not grow one: a login runner in which the
349
- <em>agent</em> picks the destination URL turns page-content prompt injection into credential
351
+ <em>agent</em> picks the destination turns page-content prompt injection into credential
350
352
  exfiltration, and a check written into a script that already holds the plaintext in its own
351
353
  environment is not a boundary — the script can simply not call it. That is what
352
- <code>canonical_origin</code> is for: https-only, stored normalized
354
+ <code>canonical_target</code> is for: stored normalized
353
355
  (<code>scheme://host[:port]</code>, no path/query/fragment/userinfo, host lowercased), so a
354
356
  separate <em>out-of-process</em> runner that the agent does not author can pin a credential to
355
- exactly one origin and check the live top-level frame before typing. That runner is
356
- deliberately not shipped in this package. <code>totp_secret</code> must be base32 — a
357
- malformed seed produces wrong codes that look exactly like a wrong password.</p>
357
+ exactly one destination and check where it is actually about to authenticate before typing.
358
+ That runner is deliberately not shipped in this package. <code>totp_secret</code> must be
359
+ base32 — a malformed seed produces wrong codes that look exactly like a wrong password.</p>
360
+
361
+ <p><strong>Not just websites (v2.118.0).</strong> A <code>login</code> holds any target with
362
+ one unambiguous destination: <code>ssh://build01.lan:22</code>,
363
+ <code>postgres://db.internal:5432</code>, <code>rdp://jump.corp</code>,
364
+ <code>smb://files.lan</code>. The scheme comes from an allowlist (https, ssh, sftp, ftps,
365
+ rdp, smb, vnc, winrm, ldaps, imaps, smtps, amqps, postgres, mysql, mariadb, mssql, mongodb,
366
+ redis); <strong>cleartext schemes are refused by name</strong> and told which TLS variant to
367
+ use instead, for the same reason http:// always was. A server login may carry a
368
+ <code>private_key</code> (PEM/OpenSSH, checked, with its own size allowance and an optional
369
+ <code>passphrase</code>) instead of a password — one of the two is required, because a login
370
+ with neither is not a credential.</p>
371
+
372
+ <p>The origin-pinning property is <em>preserved, not weakened</em>:
373
+ <code>canonical_origin</code> is still a valid field name and still reads back — but
374
+ <strong>only when the target is https</strong>. A browser broker that pins a credential by
375
+ asking for <code>canonical_origin</code> gets nothing for an <code>ssh://</code> entry and
376
+ fails closed, rather than being handed a string it cannot compare to a top-level frame.
377
+ Entries stored before v2.118.0 need no migration: the old field name is accepted on input
378
+ and resolves on read.</p>
358
379
  </section>
359
380
 
360
381
  <hr class="divider">
@@ -3111,7 +3111,7 @@ def _hub_override_row(row: dict) -> dict:
3111
3111
  quoted under the untrusted-input label, never as markup.
3112
3112
  """
3113
3113
  out = {k: row.get(k) for k in _OVERRIDE_ROW_FIELDS}
3114
- for extra in ("grant_id", "grant_mode", "muted"):
3114
+ for extra in ("grant_id", "grant_mode", "grant_scope", "muted"):
3115
3115
  if row.get(extra) is not None:
3116
3116
  out[extra] = row.get(extra)
3117
3117
  return out
@@ -3160,6 +3160,7 @@ def api_hub_overrides():
3160
3160
  whether the layer is still escalatable and which typed challenge (if any)
3161
3161
  an approval demands.
3162
3162
  """
3163
+ from services import override_grants as orq_grants
3163
3164
  from services import override_policy as opol
3164
3165
  from services import override_requests as orq
3165
3166
  project_path = ""
@@ -3199,6 +3200,12 @@ def api_hub_overrides():
3199
3200
  if policy is not None else False
3200
3201
  entry["allow_session_grants"] = bool(policy.allow_session_grants) \
3201
3202
  if policy is not None else False
3203
+ # A rule grant is only offerable when policy allows it AND the rule is
3204
+ # a real glob — the synthetic discipline/shell tokens have no path set
3205
+ # to widen along, so the card must not show a button that would 400.
3206
+ entry["allow_rule_grants"] = bool(
3207
+ policy is not None and policy.allow_rule_grants
3208
+ and orq_grants.rule_is_globbable(row.get("rule", "")))
3202
3209
  entry["project_name"] = Path(proj).name
3203
3210
  out.append(entry)
3204
3211
  return jsonify({"requests": out, "count": len(out),
@@ -3234,12 +3241,18 @@ def api_hub_override_decide(request_id):
3234
3241
 
3235
3242
  rule_class = str(row.get("rule_class") or "")
3236
3243
  confirm = data.get("confirm")
3237
- if (decision == orq.DECISION_APPROVE
3238
- and rule_class in opol.TYPED_CONFIRM_LAYERS
3244
+ # A rule-scoped approval demands the glob retyped on EVERY layer, not just
3245
+ # the two that already do — it is a standing capability, so the layers
3246
+ # that are one-tap for `once` are deliberately not one-tap for `rule`.
3247
+ needs_glob = (rule_class in opol.TYPED_CONFIRM_LAYERS
3248
+ or mode == orq.MODE_RULE)
3249
+ if (decision == orq.DECISION_APPROVE and needs_glob
3239
3250
  and confirm != row.get("rule")):
3240
3251
  return jsonify({
3241
- "error": f"approving an {rule_class} request needs the rule "
3242
- "glob retyped by hand",
3252
+ "error": ("a rule-scoped approval needs the rule glob retyped by "
3253
+ "hand" if mode == orq.MODE_RULE else
3254
+ f"approving an {rule_class} request needs the rule "
3255
+ "glob retyped by hand"),
3243
3256
  "needs_confirmation": True,
3244
3257
  "confirm_with": row.get("rule"),
3245
3258
  }), 400
@@ -3261,14 +3274,73 @@ def api_hub_override_decide(request_id):
3261
3274
  except Exception as exc:
3262
3275
  return jsonify({"error": str(exc)}), 500
3263
3276
 
3264
- _hub_override_audit(decision, result,
3265
- confirmed=rule_class in opol.TYPED_CONFIRM_LAYERS,
3277
+ _hub_override_audit(decision, result, confirmed=needs_glob,
3266
3278
  detail_extra={"mode": mode,
3267
3279
  "grant_id": result.get("grant_id", "")})
3268
3280
  return jsonify({"request": _hub_override_row(result),
3269
3281
  "decision": decision})
3270
3282
 
3271
3283
 
3284
+ #: Grant fields the hub may render. `path_key` is a canon identity, not a
3285
+ #: display string, but a rule grant's whole point is that it is NOT bound to
3286
+ #: that one path — so the card leads with the rule and shows the path only as
3287
+ #: "minted from", which is what the user was looking at when they approved.
3288
+ _GRANT_ROW_FIELDS = (
3289
+ "id", "request_id", "session_id", "scope", "layer", "rule", "tool", "op",
3290
+ "path_key", "expires_at", "uses_remaining", "idle_s", "granted_at",
3291
+ "granted_by", "last_used_at",
3292
+ )
3293
+
3294
+
3295
+ @app.route("/api/hub/grants", methods=["GET"])
3296
+ def api_hub_grants():
3297
+ """Live grants for one project — the standing capabilities, visible.
3298
+
3299
+ A rule-scoped grant can authorise many calls over hours, so it must be
3300
+ inspectable and killable from the same screen that minted it. Without
3301
+ this the only evidence a grant exists is a line in the agent's own
3302
+ transcript, which is the wrong place to police it from.
3303
+ """
3304
+ from services import override_grants as og
3305
+ raw = (request.args.get("path") or "").strip()
3306
+ if not raw:
3307
+ return jsonify({"error": "path is required"}), 400
3308
+ try:
3309
+ resolved = _resolve_project_path(raw)
3310
+ except ValueError as e:
3311
+ return jsonify({"error": str(e)}), 404
3312
+ session_id = (request.args.get("session") or "").strip()
3313
+ try:
3314
+ grants = og.active(str(resolved), session_id=session_id)
3315
+ except Exception as exc:
3316
+ return jsonify({"error": str(exc)}), 500
3317
+ out = [{k: g.get(k) for k in _GRANT_ROW_FIELDS} for g in grants]
3318
+ return jsonify({"path": str(resolved), "grants": out, "count": len(out)})
3319
+
3320
+
3321
+ @app.route("/api/hub/grants/<grant_id>", methods=["DELETE"])
3322
+ def api_hub_grant_revoke(grant_id):
3323
+ """Revoke one live grant. Human surface; ledger-logged like a decision."""
3324
+ from services import override_grants as og
3325
+ raw = (request.args.get("path") or "").strip()
3326
+ if not raw:
3327
+ return jsonify({"error": "path is required"}), 400
3328
+ try:
3329
+ resolved = _resolve_project_path(raw)
3330
+ except ValueError as e:
3331
+ return jsonify({"error": str(e)}), 404
3332
+ try:
3333
+ gone = og.revoke(str(resolved), grant_id=str(grant_id))
3334
+ except Exception as exc:
3335
+ return jsonify({"error": str(exc)}), 500
3336
+ if not gone:
3337
+ return jsonify({"error": "unknown or already-expired grant"}), 404
3338
+ _hub_override_audit("grant_revoke",
3339
+ {"id": "", "project_path": str(resolved), "rule": ""},
3340
+ detail_extra={"grant_id": str(grant_id)})
3341
+ return jsonify({"revoked": str(grant_id)})
3342
+
3343
+
3272
3344
  @app.route("/api/hub/access", methods=["GET"])
3273
3345
  def api_hub_access():
3274
3346
  """Effective Access Guard rules + override policy for one project.