code-context-control 2.125.0__tar.gz → 2.127.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 (526) hide show
  1. {code_context_control-2.125.0/code_context_control.egg-info → code_context_control-2.127.0}/PKG-INFO +1 -1
  2. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/_hook_utils.py +34 -0
  3. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/c3.py +59 -2
  4. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/commands/parser.py +12 -0
  5. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_dispatch.py +12 -3
  6. code_context_control-2.127.0/cli/hook_session_end.py +103 -0
  7. code_context_control-2.127.0/cli/hook_session_open.py +97 -0
  8. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_server.py +37 -0
  9. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_access.js +111 -11
  10. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/mcp_server.py +114 -1
  11. {code_context_control-2.125.0 → code_context_control-2.127.0/code_context_control.egg-info}/PKG-INFO +1 -1
  12. {code_context_control-2.125.0 → code_context_control-2.127.0}/code_context_control.egg-info/SOURCES.txt +9 -0
  13. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/mobile_api.py +231 -4
  14. {code_context_control-2.125.0 → code_context_control-2.127.0}/pyproject.toml +1 -1
  15. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_runner.py +32 -0
  16. code_context_control-2.127.0/services/host_sessions.py +95 -0
  17. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/notifications.py +31 -0
  18. code_context_control-2.127.0/services/override_costs.py +183 -0
  19. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/shell_jobs.py +93 -1
  20. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_codex_compat.py +11 -0
  21. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_dispatch.py +9 -0
  22. code_context_control-2.127.0/tests/test_hook_session_end.py +291 -0
  23. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_override_routes.py +73 -0
  24. code_context_control-2.127.0/tests/test_mobile_jobs_ci.py +327 -0
  25. code_context_control-2.127.0/tests/test_mobile_override_costs.py +252 -0
  26. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mobile_override_routes.py +84 -0
  27. code_context_control-2.127.0/tests/test_notification_kinds.py +294 -0
  28. code_context_control-2.127.0/tests/test_override_costs.py +320 -0
  29. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_jobs_s3.py +4 -1
  30. {code_context_control-2.125.0 → code_context_control-2.127.0}/LICENSE +0 -0
  31. {code_context_control-2.125.0 → code_context_control-2.127.0}/README.md +0 -0
  32. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/__init__.py +0 -0
  33. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/_shell_writes.py +0 -0
  34. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/commands/__init__.py +0 -0
  35. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/commands/common.py +0 -0
  36. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/docs.html +0 -0
  37. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/edits.html +0 -0
  38. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/access.html +0 -0
  39. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/bitbucket.html +0 -0
  40. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/credentials.html +0 -0
  41. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/getting-started.html +0 -0
  42. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/index.html +0 -0
  43. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/masking.html +0 -0
  44. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/oracle.html +0 -0
  45. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/shared.css +0 -0
  46. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/tools.html +0 -0
  47. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/guide/workflow.html +0 -0
  48. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_access_guard.py +0 -0
  49. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_artifact.py +0 -0
  50. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_auto_snapshot.py +0 -0
  51. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_c3_signal.py +0 -0
  52. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_c3read.py +0 -0
  53. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_codex_lifecycle.py +0 -0
  54. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_edit_ledger.py +0 -0
  55. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_edit_unlock.py +0 -0
  56. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_filter.py +0 -0
  57. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_ghost_files.py +0 -0
  58. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_pretool_enforce.py +0 -0
  59. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_prompt_recall.py +0 -0
  60. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_read.py +0 -0
  61. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_session_stats.py +0 -0
  62. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hook_terse_advisor.py +0 -0
  63. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/app.js +0 -0
  64. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/add_project.js +0 -0
  65. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/config_editor.js +0 -0
  66. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  67. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_health.js +0 -0
  68. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_panel.js +0 -0
  69. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  70. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  71. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/drill_views.js +0 -0
  72. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/global_search.js +0 -0
  73. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_ci.js +0 -0
  74. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  75. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  76. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  77. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_locks.js +0 -0
  78. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  79. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  80. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/modals.js +0 -0
  81. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/project_card.js +0 -0
  82. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/project_tree.js +0 -0
  83. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/session_drawer.js +0 -0
  84. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/settings_modal.js +0 -0
  85. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/sidebar.js +0 -0
  86. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/summary_bar.js +0 -0
  87. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/task_board.js +0 -0
  88. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/toasts.js +0 -0
  89. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/components/topbar.js +0 -0
  90. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui/state.js +0 -0
  91. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/hub_ui.html +0 -0
  92. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/mcp_proxy.py +0 -0
  93. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/progress.py +0 -0
  94. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/server.py +0 -0
  95. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/__init__.py +0 -0
  96. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/_grants.py +0 -0
  97. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/_helpers.py +0 -0
  98. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/agent.py +0 -0
  99. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/artifacts.py +0 -0
  100. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/bitbucket.py +0 -0
  101. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/ci.py +0 -0
  102. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/compress.py +0 -0
  103. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/credentials.py +0 -0
  104. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/delegate.py +0 -0
  105. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/edit.py +0 -0
  106. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/edit_verify.py +0 -0
  107. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/edits.py +0 -0
  108. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/federate.py +0 -0
  109. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/filter.py +0 -0
  110. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/impact.py +0 -0
  111. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/jira.py +0 -0
  112. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/locks.py +0 -0
  113. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/memory.py +0 -0
  114. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/override.py +0 -0
  115. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/project.py +0 -0
  116. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/read.py +0 -0
  117. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/search.py +0 -0
  118. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/session.py +0 -0
  119. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/shell.py +0 -0
  120. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/shell_job.py +0 -0
  121. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/shell_nudge.py +0 -0
  122. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/shell_parsers.py +0 -0
  123. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/shell_render.py +0 -0
  124. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/status.py +0 -0
  125. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/tasks.py +0 -0
  126. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/tools/validate.py +0 -0
  127. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/api.js +0 -0
  128. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/app.js +0 -0
  129. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/access.js +0 -0
  130. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/bitbucket.js +0 -0
  131. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/chat.js +0 -0
  132. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/credentials.js +0 -0
  133. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/dashboard.js +0 -0
  134. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/edits.js +0 -0
  135. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/enforcement.js +0 -0
  136. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/instructions.js +0 -0
  137. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/jira.js +0 -0
  138. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/memory.js +0 -0
  139. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/sessions.js +0 -0
  140. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/settings.js +0 -0
  141. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/sidebar.js +0 -0
  142. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/components/tasks.js +0 -0
  143. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/icons.js +0 -0
  144. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/pm_shared.js +0 -0
  145. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/shared.js +0 -0
  146. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui/theme.js +0 -0
  147. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui.html +0 -0
  148. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui_legacy.html +0 -0
  149. {code_context_control-2.125.0 → code_context_control-2.127.0}/cli/ui_nano.html +0 -0
  150. {code_context_control-2.125.0 → code_context_control-2.127.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  151. {code_context_control-2.125.0 → code_context_control-2.127.0}/code_context_control.egg-info/entry_points.txt +0 -0
  152. {code_context_control-2.125.0 → code_context_control-2.127.0}/code_context_control.egg-info/requires.txt +0 -0
  153. {code_context_control-2.125.0 → code_context_control-2.127.0}/code_context_control.egg-info/top_level.txt +0 -0
  154. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/__init__.py +0 -0
  155. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/codex_patch.py +0 -0
  156. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/config.py +0 -0
  157. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/host.py +0 -0
  158. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/ide.py +0 -0
  159. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/mcp_toml.py +0 -0
  160. {code_context_control-2.125.0 → code_context_control-2.127.0}/core/web_security.py +0 -0
  161. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/__init__.py +0 -0
  162. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/config.py +0 -0
  163. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/listeners.py +0 -0
  164. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/mcp_oracle.py +0 -0
  165. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/oracle_server.py +0 -0
  166. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/oracle_ui.html +0 -0
  167. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/__init__.py +0 -0
  168. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/activity_reporter.py +0 -0
  169. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/api_auth.py +0 -0
  170. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/c3_bridge.py +0 -0
  171. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/chat_engine.py +0 -0
  172. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/chat_poll.py +0 -0
  173. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/chat_store.py +0 -0
  174. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/client_tokens.py +0 -0
  175. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/cross_memory.py +0 -0
  176. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/discovery_audit.py +0 -0
  177. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/federated_graph.py +0 -0
  178. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/health_checker.py +0 -0
  179. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/insight_engine.py +0 -0
  180. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/local_session.py +0 -0
  181. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/memory_reader.py +0 -0
  182. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/memory_writer.py +0 -0
  183. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/project_scanner.py +0 -0
  184. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/review_agent.py +0 -0
  185. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/tool_executor.py +0 -0
  186. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/services/tool_registry.py +0 -0
  187. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/activity.js +0 -0
  188. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/agents.js +0 -0
  189. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/app.js +0 -0
  190. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/busy.js +0 -0
  191. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/conversations.js +0 -0
  192. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/input.js +0 -0
  193. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/markdown.js +0 -0
  194. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/send.js +0 -0
  195. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/stream_renderer.js +0 -0
  196. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/chat/toolbar.js +0 -0
  197. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/core.js +0 -0
  198. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/crossgraph.js +0 -0
  199. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/header.js +0 -0
  200. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/insights.js +0 -0
  201. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/projects.js +0 -0
  202. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/qrcode.js +0 -0
  203. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/settings.js +0 -0
  204. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/suggestions.js +0 -0
  205. {code_context_control-2.125.0 → code_context_control-2.127.0}/oracle/ui/theme_tabs.js +0 -0
  206. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/__init__.py +0 -0
  207. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/access_guard.py +0 -0
  208. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/access_telemetry.py +0 -0
  209. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/activity_log.py +0 -0
  210. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/agent_base.py +0 -0
  211. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/agent_locks.py +0 -0
  212. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/agents.py +0 -0
  213. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/artifact_defs.py +0 -0
  214. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/artifact_store.py +0 -0
  215. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/atomic_json.py +0 -0
  216. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/auto_memory.py +0 -0
  217. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/background_service.py +0 -0
  218. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/__init__.py +0 -0
  219. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/external/__init__.py +0 -0
  220. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/external/aider_polyglot.py +0 -0
  221. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/external/swe_bench.py +0 -0
  222. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/map_eval.py +0 -0
  223. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/search_eval.py +0 -0
  224. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bench/shell_eval.py +0 -0
  225. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/benchmark_dashboard.py +0 -0
  226. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bitbucket_client.py +0 -0
  227. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/bitbucket_credentials.py +0 -0
  228. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_act.py +0 -0
  229. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_cache.py +0 -0
  230. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_expr.py +0 -0
  231. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_failures.py +0 -0
  232. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_github.py +0 -0
  233. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_impact.py +0 -0
  234. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_intel.py +0 -0
  235. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ci_workflow.py +0 -0
  236. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/circuit_breaker.py +0 -0
  237. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/claude_md.py +0 -0
  238. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/codex_integration.py +0 -0
  239. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/codex_transcript.py +0 -0
  240. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/compressor.py +0 -0
  241. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/context_snapshot.py +0 -0
  242. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/conversation_store.py +0 -0
  243. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/cred_audit.py +0 -0
  244. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/cred_telemetry.py +0 -0
  245. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/credential_store.py +0 -0
  246. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/dir_map.py +0 -0
  247. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/doc_index.py +0 -0
  248. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/e2e_benchmark.py +0 -0
  249. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/e2e_evaluator.py +0 -0
  250. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/e2e_tasks.py +0 -0
  251. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/edit_ledger.py +0 -0
  252. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/embedding_index.py +0 -0
  253. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/enforcement_policy.py +0 -0
  254. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/error_reporting.py +0 -0
  255. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/file_map.py +0 -0
  256. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/file_memory.py +0 -0
  257. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/git_context.py +0 -0
  258. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/hub_service.py +0 -0
  259. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/index_store.py +0 -0
  260. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/indexer.py +0 -0
  261. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/jira_client.py +0 -0
  262. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/jira_cloud.py +0 -0
  263. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/jira_credentials.py +0 -0
  264. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/jira_data_center.py +0 -0
  265. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/jira_links.py +0 -0
  266. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/lexical_index.py +0 -0
  267. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/mask_activation.py +0 -0
  268. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/mask_mirror.py +0 -0
  269. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/mask_presets.py +0 -0
  270. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory.py +0 -0
  271. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_consolidator.py +0 -0
  272. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_distiller.py +0 -0
  273. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_graph.py +0 -0
  274. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_grounder.py +0 -0
  275. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_queue.py +0 -0
  276. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/memory_scorer.py +0 -0
  277. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/metrics.py +0 -0
  278. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ollama_bridge.py +0 -0
  279. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ollama_client.py +0 -0
  280. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/ollama_credentials.py +0 -0
  281. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/oracle_service.py +0 -0
  282. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/output_filter.py +0 -0
  283. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/override_grants.py +0 -0
  284. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/override_policy.py +0 -0
  285. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/override_requests.py +0 -0
  286. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/override_wake.py +0 -0
  287. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/parser.py +0 -0
  288. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/project_manager.py +0 -0
  289. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/project_runtime.py +0 -0
  290. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/protocol.py +0 -0
  291. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/proxy_state.py +0 -0
  292. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/repo_map.py +0 -0
  293. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/repo_shape.py +0 -0
  294. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/reranker.py +0 -0
  295. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/retention.py +0 -0
  296. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/retrieval.py +0 -0
  297. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/retrieval_broker.py +0 -0
  298. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/router.py +0 -0
  299. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/runtime.py +0 -0
  300. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/scanner.py +0 -0
  301. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/session_benchmark.py +0 -0
  302. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/session_manager.py +0 -0
  303. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/session_preloader.py +0 -0
  304. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/shell_output.py +0 -0
  305. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/subprojects.py +0 -0
  306. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/task_store.py +0 -0
  307. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/telemetry.py +0 -0
  308. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/text_index.py +0 -0
  309. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/time_tracker.py +0 -0
  310. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/tool_classifier.py +0 -0
  311. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/transcript_index.py +0 -0
  312. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/validation_cache.py +0 -0
  313. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/vector_store.py +0 -0
  314. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/watcher.py +0 -0
  315. {code_context_control-2.125.0 → code_context_control-2.127.0}/services/win_subprocess.py +0 -0
  316. {code_context_control-2.125.0 → code_context_control-2.127.0}/setup.cfg +0 -0
  317. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_builtin_optout.py +0 -0
  318. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_guard.py +0 -0
  319. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_guard_meta.py +0 -0
  320. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_guard_shell_project.py +0 -0
  321. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_guard_surfaces.py +0 -0
  322. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_access_guard_wiring.py +0 -0
  323. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_activity_reporter.py +0 -0
  324. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_agent_locks.py +0 -0
  325. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_aider_polyglot.py +0 -0
  326. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_antigravity_transition.py +0 -0
  327. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_artifact_store.py +0 -0
  328. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_artifact_tool.py +0 -0
  329. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_atomic_json.py +0 -0
  330. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_background_service.py +0 -0
  331. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  332. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_bitbucket_client.py +0 -0
  333. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_bitbucket_config_fallback.py +0 -0
  334. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_bitbucket_credentials.py +0 -0
  335. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_bitbucket_tool.py +0 -0
  336. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_builtin_modes.py +0 -0
  337. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  338. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_c3_shell.py +0 -0
  339. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_chat_poll.py +0 -0
  340. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_act.py +0 -0
  341. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_cache.py +0 -0
  342. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_expr.py +0 -0
  343. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_impact.py +0 -0
  344. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_intel.py +0 -0
  345. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_runner.py +0 -0
  346. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ci_workflow.py +0 -0
  347. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_circuit_breaker.py +0 -0
  348. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_claude_md_merge.py +0 -0
  349. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_claude_md_updater.py +0 -0
  350. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_cli_smoke.py +0 -0
  351. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_codex_mcp_transport.py +0 -0
  352. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_codex_native.py +0 -0
  353. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_compressor_large_file.py +0 -0
  354. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_confirm_guard.py +0 -0
  355. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_confirm_guard_wiring.py +0 -0
  356. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_cooccurrence_bounds.py +0 -0
  357. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_cred_audit.py +0 -0
  358. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_cred_telemetry.py +0 -0
  359. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_credential_login_kind.py +0 -0
  360. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_credential_store.py +0 -0
  361. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_credential_ui_parity.py +0 -0
  362. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_credentials_routes.py +0 -0
  363. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_credentials_tool.py +0 -0
  364. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_creds_cli_smoke.py +0 -0
  365. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_delegate_cascade.py +0 -0
  366. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_delegate_version_probe.py +0 -0
  367. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_dir_map_c3.py +0 -0
  368. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_e2e_benchmark.py +0 -0
  369. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_edit_ledger_deadline.py +0 -0
  370. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_edit_ledger_hook.py +0 -0
  371. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_edit_locking.py +0 -0
  372. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_edit_normalization.py +0 -0
  373. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_edit_verify.py +0 -0
  374. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_embedding_index_deadlock.py +0 -0
  375. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_enforcement_flip.py +0 -0
  376. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_enforcement_policy.py +0 -0
  377. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_enforcement_routes.py +0 -0
  378. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_env_parser.py +0 -0
  379. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  380. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_federated_graph.py +0 -0
  381. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_file_map_c1.py +0 -0
  382. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_filter_backoff.py +0 -0
  383. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ghost_files.py +0 -0
  384. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ghost_generation.py +0 -0
  385. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_git_branch_awareness.py +0 -0
  386. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_access_guard.py +0 -0
  387. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_pretool_enforce.py +0 -0
  388. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_prompt_recall.py +0 -0
  389. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_smoke.py +0 -0
  390. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_state.py +0 -0
  391. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hook_utf8_stdio.py +0 -0
  392. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_credentials_route.py +0 -0
  393. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_credentials_routes_write.py +0 -0
  394. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_enforcement_routes.py +0 -0
  395. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_inspect_api.py +0 -0
  396. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_locks_routes.py +0 -0
  397. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_oracle_service_routes.py +0 -0
  398. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_server_smoke.py +0 -0
  399. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_hub_subproject_links.py +0 -0
  400. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ide_workflow_adaptation.py +0 -0
  401. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  402. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  403. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_init_scan.py +0 -0
  404. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_install_mcp_entrypoint.py +0 -0
  405. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_cli_smoke.py +0 -0
  406. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_client.py +0 -0
  407. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_config_fallback.py +0 -0
  408. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_credentials.py +0 -0
  409. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_links.py +0 -0
  410. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_routes.py +0 -0
  411. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_jira_tool.py +0 -0
  412. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_large_files_c4.py +0 -0
  413. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_lazy_store_init.py +0 -0
  414. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_map_eval.py +0 -0
  415. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_map_telemetry_c0.py +0 -0
  416. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mask_guard.py +0 -0
  417. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mask_routes.py +0 -0
  418. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mask_surfaces.py +0 -0
  419. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mcp_host_guard.py +0 -0
  420. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mcp_memory_timeout.py +0 -0
  421. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mcp_server_smoke.py +0 -0
  422. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mcp_toml.py +0 -0
  423. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_memory_distiller.py +0 -0
  424. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_memory_graph_api.py +0 -0
  425. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_memory_queue.py +0 -0
  426. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_memory_system.py +0 -0
  427. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mobile_api.py +0 -0
  428. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mobile_clients.py +0 -0
  429. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mobile_extras.py +0 -0
  430. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_mobile_security_api.py +0 -0
  431. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_no_stale_model_pins.py +0 -0
  432. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_notification_dedup.py +0 -0
  433. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_notification_discipline.py +0 -0
  434. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ollama_credentials.py +0 -0
  435. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_api_auth.py +0 -0
  436. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_apikey_api.py +0 -0
  437. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_c3_bridge.py +0 -0
  438. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_chat_engine.py +0 -0
  439. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_chat_store.py +0 -0
  440. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_discovery_api.py +0 -0
  441. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_discovery_audit.py +0 -0
  442. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_health_probe.py +0 -0
  443. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_local_auth.py +0 -0
  444. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_loopback.py +0 -0
  445. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_ollama_bridge.py +0 -0
  446. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_scanner_cache.py +0 -0
  447. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_security_fixes.py +0 -0
  448. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_session_bootstrap.py +0 -0
  449. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_subproject_awareness.py +0 -0
  450. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_oracle_ui_bundle.py +0 -0
  451. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_output_filter.py +0 -0
  452. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_override_grants.py +0 -0
  453. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_override_grants_mcp.py +0 -0
  454. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_override_requests.py +0 -0
  455. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_override_wake.py +0 -0
  456. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_permissions.py +0 -0
  457. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_pm_api.py +0 -0
  458. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_project_manager.py +0 -0
  459. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_project_manager_merge.py +0 -0
  460. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_project_tool.py +0 -0
  461. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_read_coercion.py +0 -0
  462. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_read_edit_parity.py +0 -0
  463. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_related_facts.py +0 -0
  464. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_repo_map.py +0 -0
  465. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_repo_shape.py +0 -0
  466. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_response_boilerplate.py +0 -0
  467. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_retention.py +0 -0
  468. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_review_digest.py +0 -0
  469. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_eval.py +0 -0
  470. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p1.py +0 -0
  471. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p2.py +0 -0
  472. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p2b.py +0 -0
  473. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p3.py +0 -0
  474. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p4.py +0 -0
  475. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_search_p5.py +0 -0
  476. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_service_durability.py +0 -0
  477. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_session_benchmark.py +0 -0
  478. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_session_budget.py +0 -0
  479. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_budget_s1.py +0 -0
  480. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_creds.py +0 -0
  481. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_eval.py +0 -0
  482. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_nudge_s4.py +0 -0
  483. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_output.py +0 -0
  484. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_parsers_s2.py +0 -0
  485. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_robustness.py +0 -0
  486. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_telemetry_s0.py +0 -0
  487. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_write_ledger.py +0 -0
  488. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_shell_writes.py +0 -0
  489. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subproject_clear_scope.py +0 -0
  490. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subproject_depth_consumers.py +0 -0
  491. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subproject_exclusion.py +0 -0
  492. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subproject_federation.py +0 -0
  493. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subproject_linkage_api.py +0 -0
  494. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_subprojects.py +0 -0
  495. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_swe_bench.py +0 -0
  496. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_task_store.py +0 -0
  497. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_task_tool.py +0 -0
  498. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_time_tracker.py +0 -0
  499. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_token_telemetry.py +0 -0
  500. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_token_tracking.py +0 -0
  501. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_tool_registry.py +0 -0
  502. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_ui_jsx_syntax.py +0 -0
  503. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_upgrade_and_version.py +0 -0
  504. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_validate.py +0 -0
  505. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_vault_write_guard.py +0 -0
  506. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_version_sync.py +0 -0
  507. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_web_security.py +0 -0
  508. {code_context_control-2.125.0 → code_context_control-2.127.0}/tests/test_windows_reliability.py +0 -0
  509. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/__init__.py +0 -0
  510. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/backend.py +0 -0
  511. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/main.py +0 -0
  512. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/__init__.py +0 -0
  513. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/benchmark_view.py +0 -0
  514. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/claudemd_view.py +0 -0
  515. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/compress_view.py +0 -0
  516. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/index_view.py +0 -0
  517. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/init_view.py +0 -0
  518. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/mcp_view.py +0 -0
  519. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/optimize_view.py +0 -0
  520. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/pipe_view.py +0 -0
  521. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/projects_view.py +0 -0
  522. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/search_view.py +0 -0
  523. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/session_view.py +0 -0
  524. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/stats.py +0 -0
  525. {code_context_control-2.125.0 → code_context_control-2.127.0}/tui/screens/ui_view.py +0 -0
  526. {code_context_control-2.125.0 → code_context_control-2.127.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.125.0
3
+ Version: 2.127.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
@@ -500,6 +500,40 @@ def record_json_unlocks(
500
500
  pass
501
501
 
502
502
 
503
+ _PROJECT_WALK_UP_LEVELS = 4
504
+
505
+
506
+ def find_project(payload: dict, project_path=None) -> Path | None:
507
+ """The nearest ancestor (≤ 4 levels) of ``project_path`` / ``payload.cwd``
508
+ that owns a ``.c3`` directory, or None.
509
+
510
+ Claude Code runs hooks from the project root, Codex passes ``--project``;
511
+ either way the payload's ``cwd`` may be a subdirectory (a session opened
512
+ in ``src/``). The walk is lexical on purpose — no path canonicalisation
513
+ happens here (see tests/test_access_guard_meta.py).
514
+ """
515
+ candidates = []
516
+ if project_path:
517
+ candidates.append(Path(str(project_path)))
518
+ cwd = payload.get("cwd") if isinstance(payload, dict) else None
519
+ if cwd:
520
+ candidates.append(Path(str(cwd)))
521
+ if not candidates:
522
+ candidates.append(Path.cwd())
523
+ for base in candidates:
524
+ node = base
525
+ for _ in range(_PROJECT_WALK_UP_LEVELS):
526
+ try:
527
+ if (node / ".c3").is_dir():
528
+ return node
529
+ except OSError:
530
+ break
531
+ if node.parent == node:
532
+ break
533
+ node = node.parent
534
+ return None
535
+
536
+
503
537
  def emit_additional_context(text: str, is_gemini: bool) -> None:
504
538
  """Write additionalContext JSON to stdout in the correct format for the IDE."""
505
539
  if is_gemini:
@@ -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.125.0"
95
+ __version__ = "2.127.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -5558,6 +5558,8 @@ def cmd_install_mcp(args):
5558
5558
  hook_posttool_cmd = f"{_dispatch_base} posttool"
5559
5559
  hook_stop_cmd = f"{_dispatch_base} stop"
5560
5560
  hook_prompt_cmd = f"{_dispatch_base} prompt"
5561
+ hook_start_cmd = f"{_dispatch_base} start"
5562
+ hook_end_cmd = f"{_dispatch_base} end"
5561
5563
 
5562
5564
  shell_matcher = "Bash"
5563
5565
  read_matcher = "Read"
@@ -5696,6 +5698,25 @@ def cmd_install_mcp(args):
5696
5698
  )
5697
5699
  settings.setdefault("hooks", {})[prompt_event] = existing_prompt
5698
5700
 
5701
+ # ── SessionStart / SessionEnd hooks (v2.126.0: session_open /
5702
+ # session_end activity rows + `session` notifications, the join key
5703
+ # a desktop client needs). Same merge discipline: replace only C3's
5704
+ # own dispatcher entries, keep user-added ones. SessionStart's
5705
+ # matcher is the payload `source` (startup|resume|clear|compact);
5706
+ # empty matches all — the hook itself ignores `compact`.
5707
+ for _lifecycle_event, _lifecycle_cmd in (
5708
+ ("SessionStart", hook_start_cmd),
5709
+ ("SessionEnd", hook_end_cmd),
5710
+ ):
5711
+ _kept = [
5712
+ h for h in settings.get("hooks", {}).get(_lifecycle_event, [])
5713
+ if not _is_c3_prompt_hook(h)
5714
+ ]
5715
+ _kept.append(
5716
+ {"matcher": "", "hooks": [{"type": "command", "command": _lifecycle_cmd}]}
5717
+ )
5718
+ settings.setdefault("hooks", {})[_lifecycle_event] = _kept
5719
+
5699
5720
  # Claude Code only: enable MCP server prompt settings
5700
5721
  if profile.name == "claude-code":
5701
5722
  settings["enableAllProjectMcpServers"] = True
@@ -5742,6 +5763,7 @@ def cmd_install_mcp(args):
5742
5763
  print(f" Hooks ({hook_event}): dispatcher (1 spawn/event) — filter/ghost/read-guard/ledger/unlock/signal via cli/hook_dispatch.py posttool")
5743
5764
  print(f" Hooks ({pre_event}): dispatcher — {read_matcher}/{grep_matcher}/{glob_matcher}/{edit_matcher}/{write_matcher} (c3 enforcement)")
5744
5765
  print(" Hooks (Stop): dispatcher — session_stats + auto_snapshot + terse_advisor")
5766
+ print(" Hooks (SessionStart/SessionEnd): dispatcher — session_open / session_end rows + session notifications")
5745
5767
  if profile.name == "claude-code":
5746
5768
  print(" Claude MCP prompt settings enabled for this project")
5747
5769
  if perm_tier and profile.name == "claude-code":
@@ -6946,8 +6968,10 @@ def cmd_override(args):
6946
6968
  """
6947
6969
  sub = getattr(args, "override_cmd", None)
6948
6970
  if not sub:
6949
- print("Usage: c3 override <policy|grant|list|revoke|check|sweep> ...")
6971
+ print("Usage: c3 override <policy|grant|list|revoke|check|sweep|"
6972
+ "requests|approve|deny|costs> ...")
6950
6973
  print(" c3 override policy # what is escalatable right now")
6974
+ print(" c3 override costs # which rules keep holding the agent")
6951
6975
  return
6952
6976
 
6953
6977
  from services import override_grants as og
@@ -6976,6 +7000,39 @@ def cmd_override(args):
6976
7000
  _override_requests(args, project_path)
6977
7001
  elif sub in ("approve", "deny"):
6978
7002
  _override_decide(args, sub, project_path)
7003
+ elif sub == "costs":
7004
+ _override_costs(args, project_path)
7005
+
7006
+
7007
+ def _override_costs(args, project_path: str) -> None:
7008
+ """`c3 override costs` — which rules keep holding the agent (§11 T5)."""
7009
+ from services import override_costs as ocost
7010
+
7011
+ days = max(1, int(getattr(args, "days", None) or ocost.DEFAULT_DAYS))
7012
+ everywhere = bool(getattr(args, "all_projects", False))
7013
+ rows = ocost.rule_costs(project_path=None if everywhere else project_path,
7014
+ days=days)
7015
+ print_header(f"Override costs — last {days} day(s)"
7016
+ + ("" if everywhere else f" · {Path(project_path).name}"))
7017
+ if not rows:
7018
+ print(" (no requests in the window)")
7019
+ return
7020
+ width = max(len(r["rule"]) for r in rows)
7021
+ print(f" {'held':>4} {'ok':>3} {'no':>3} {'exp':>3} {'wait':>4} "
7022
+ f"{'rule':<{width}} suggestion")
7023
+ for r in rows:
7024
+ line = (f" {r['count']:>4} {r['approved']:>3} {r['denied']:>3} "
7025
+ f"{r['expired']:>3} {r['pending']:>4} {r['rule']:<{width}} "
7026
+ f"{r['suggestion']}")
7027
+ if r["count"] >= ocost.NUDGE_THRESHOLD:
7028
+ line += " <- costing you"
7029
+ print(line)
7030
+ if everywhere:
7031
+ print(f" {'':22s} {r['project_path']}")
7032
+ print("\n held = requests filed; ok/no/exp/wait = approved/denied/"
7033
+ "expired/still pending (withdrawn counts in held only).")
7034
+ print(" A rule you keep approving is friction: edit it with `c3 access`. "
7035
+ "A rule you keep denying is working — mute the session instead.")
6979
7036
 
6980
7037
 
6981
7038
  def _override_requests(args, project_path: str) -> None:
@@ -779,6 +779,18 @@ def build_parser(version: str, parse_cli_ide_arg):
779
779
  ov_deny.add_argument("--path", dest="project_path", default=".",
780
780
  help="Project directory (default: current)")
781
781
 
782
+ # Costs: which rules keep asking (spec §11 Threat 5, v2.127.0). A read
783
+ # over the request store — it decides nothing and edits no rule.
784
+ ov_costs = override_subs.add_parser(
785
+ "costs", help="Which rules held the agent most, and how you answered"
786
+ )
787
+ ov_costs.add_argument("--days", type=int, default=7,
788
+ help="Trailing window in days (default 7)")
789
+ ov_costs.add_argument("--all", dest="all_projects", action="store_true",
790
+ help="Every project, not just this one")
791
+ ov_costs.add_argument("--path", dest="project_path", default=".",
792
+ help="Project directory (default: current)")
793
+
782
794
  # ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
783
795
  # LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
784
796
  # `c3 access` (path policy — a security boundary) and from
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """Per-event hook dispatcher: ONE process per Claude Code / Gemini hook event.
3
3
 
4
- Usage: python hook_dispatch.py <pretool|posttool|stop|prompt>
4
+ Usage: python hook_dispatch.py <pretool|posttool|stop|prompt|start|compact|end>
5
5
 
6
6
  Before v2.42 every hook was registered as its own "cmd /c python <hook>.py"
7
7
  subprocess, so a single native Read fired up to three interpreter spawns
@@ -167,8 +167,17 @@ def _routes(event: str, raw_tool: str, norm_tool: str, host: str = HOST_CLAUDE):
167
167
  yield "hook_terse_advisor"
168
168
  elif event == "prompt":
169
169
  yield "hook_prompt_recall"
170
- elif event in ("start", "compact", "end") and host == HOST_CODEX:
171
- yield "hook_codex_lifecycle"
170
+ elif event in ("start", "compact", "end"):
171
+ # Codex's thread-bound handoff runs first (it may return context on
172
+ # SessionStart); the session bookkeeping below is host-agnostic and
173
+ # returns nothing (v2.126.0: session_open / session_end rows + the
174
+ # `session` notifications a desktop client wakes on).
175
+ if host == HOST_CODEX:
176
+ yield "hook_codex_lifecycle"
177
+ if event == "start":
178
+ yield "hook_session_open"
179
+ elif event == "end":
180
+ yield "hook_session_end"
172
181
 
173
182
 
174
183
  _RUN_CACHE: dict = {}
@@ -0,0 +1,103 @@
1
+ """SessionEnd hook: record that a host session ended (D0b, v2.126.0).
2
+
3
+ Claude Code's ``SessionEnd`` event delivers on stdin::
4
+
5
+ {
6
+ "session_id": "<Claude Code UUID>",
7
+ "transcript_path": "...",
8
+ "cwd": "...",
9
+ "hook_event_name": "SessionEnd",
10
+ "reason": "clear" | "logout" | "prompt_input_exit" | "other"
11
+ }
12
+
13
+ Codex's ``SessionEnd`` carries its thread id in the same ``session_id``
14
+ field; the dispatcher routes both here (``hook_dispatch._routes``, event
15
+ ``end``).
16
+
17
+ Why this exists: this repo's activity log held 525 ``session_start`` rows
18
+ and 14 ``session_save`` rows — ``session_save`` is only written when a human
19
+ ends a session from the hub, so for a client the end of a conversation was
20
+ invisible. Two records fix that:
21
+
22
+ - ``{type: "session_end", session_id: <C3 id if known>, host_session_id,
23
+ reason, source}`` appended to ``.c3/activity_log.jsonl``. The C3 id comes
24
+ from the link file the MCP server wrote at session start
25
+ (``services.host_sessions``); it is ``""`` when the server never learned
26
+ the host id.
27
+ - a ``session`` notification (``kind="session"``, ``ref_id=host id``,
28
+ severity ``info``, title ``Session ended <short id>``) in
29
+ ``.c3/notifications.jsonl`` — the write that wakes a ``/feed?wait=``
30
+ client.
31
+
32
+ Runs in a short-lived subprocess with a few seconds of budget; every step is
33
+ best-effort and nothing here can fail the session that is already ending.
34
+ """
35
+ from __future__ import annotations
36
+
37
+ import sys
38
+ from pathlib import Path
39
+
40
+ sys.path.insert(0, str(Path(__file__).parent.parent))
41
+
42
+ from cli._hook_utils import detect_host, find_project, log_hook_error # noqa: E402
43
+
44
+ SHORT_ID_CHARS = 8
45
+
46
+
47
+ def short_id(session_id: str) -> str:
48
+ return str(session_id or "")[:SHORT_ID_CHARS] or "?"
49
+
50
+
51
+ def run(payload: dict, project_path: Path | None = None):
52
+ """Core logic — importable by the dispatcher and tests. Returns None."""
53
+ project = find_project(payload, project_path)
54
+ if project is None:
55
+ return None
56
+ host = detect_host(payload)
57
+ host_sid = str(payload.get("session_id") or "").strip()
58
+ reason = str(payload.get("reason") or "").strip()
59
+ c3_sid = ""
60
+ try:
61
+ from services.host_sessions import linked_c3_session_id
62
+ c3_sid = linked_c3_session_id(project, host, host_sid)
63
+ except Exception:
64
+ c3_sid = ""
65
+ try:
66
+ from services.activity_log import ActivityLog
67
+ ActivityLog(str(project)).log("session_end", {
68
+ "session_id": c3_sid,
69
+ "host_session_id": host_sid,
70
+ "reason": reason,
71
+ "source": host,
72
+ })
73
+ except Exception as exc:
74
+ log_hook_error("hook_session_end", exc)
75
+ try:
76
+ from services.notifications import notify
77
+ notify(project, agent="session", severity="info",
78
+ title=f"Session ended {short_id(host_sid)}",
79
+ message=(f"{host} session {host_sid or '?'} ended"
80
+ + (f" ({reason})" if reason else "")
81
+ + (f"; C3 session {c3_sid}" if c3_sid else "")),
82
+ kind="session", ref_id=host_sid)
83
+ except Exception:
84
+ pass
85
+ return None
86
+
87
+
88
+ def main() -> None:
89
+ import json
90
+ try:
91
+ data = json.load(sys.stdin)
92
+ except Exception as exc:
93
+ log_hook_error("hook_session_end", exc)
94
+ sys.exit(0)
95
+ try:
96
+ run(data)
97
+ except Exception as exc:
98
+ log_hook_error("hook_session_end", exc)
99
+ sys.exit(0)
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()
@@ -0,0 +1,97 @@
1
+ """SessionStart hook: record that a host session opened (D0b, v2.126.0).
2
+
3
+ Claude Code's ``SessionStart`` event delivers on stdin::
4
+
5
+ {
6
+ "session_id": "<Claude Code UUID>",
7
+ "transcript_path": "...",
8
+ "cwd": "...",
9
+ "hook_event_name": "SessionStart",
10
+ "source": "startup" | "resume" | "clear" | "compact"
11
+ }
12
+
13
+ Codex's ``SessionStart`` carries its thread id in the same ``session_id``
14
+ field and no ``source``; the dispatcher routes both here (event ``start``).
15
+
16
+ Records:
17
+
18
+ - ``{type: "session_open", host_session_id, source: <host>, start_source}``
19
+ in ``.c3/activity_log.jsonl``. ``source`` is the HOST (``claude`` /
20
+ ``codex`` / ``gemini``), matching ``session_end``; the payload's own
21
+ ``source`` (startup / resume / clear) is kept as ``start_source``.
22
+ - a ``session`` notification (``kind="session"``, ``ref_id=host id``,
23
+ ``info``, title ``Session started <short id>``).
24
+
25
+ ``source: "compact"`` writes NOTHING: a compaction is the same session and
26
+ the same MCP process, and a "Session started" toast on every compaction
27
+ would teach the user to ignore the real ones.
28
+
29
+ The MCP server's own ``session_start`` row is untouched — it carries the C3
30
+ id and, when the host put its id in the environment, ``host_session_id``
31
+ too. This hook is what lets a client see the session before the runtime has
32
+ finished starting (~seconds), and it is the row the desktop's "MCP failed to
33
+ connect" sentinel starts its 30 s clock from.
34
+
35
+ Returns None always: SessionStart hook stdout becomes model context, and
36
+ this hook has nothing to tell the model.
37
+ """
38
+ from __future__ import annotations
39
+
40
+ import sys
41
+ from pathlib import Path
42
+
43
+ sys.path.insert(0, str(Path(__file__).parent.parent))
44
+
45
+ from cli._hook_utils import detect_host, find_project, log_hook_error # noqa: E402
46
+ from cli.hook_session_end import short_id # noqa: E402
47
+
48
+ _SILENT_SOURCES = {"compact"}
49
+
50
+
51
+ def run(payload: dict, project_path: Path | None = None):
52
+ """Core logic — importable by the dispatcher and tests. Returns None."""
53
+ start_source = str(payload.get("source") or "").strip().lower()
54
+ if start_source in _SILENT_SOURCES:
55
+ return None
56
+ project = find_project(payload, project_path)
57
+ if project is None:
58
+ return None
59
+ host = detect_host(payload)
60
+ host_sid = str(payload.get("session_id") or "").strip()
61
+ try:
62
+ from services.activity_log import ActivityLog
63
+ ActivityLog(str(project)).log("session_open", {
64
+ "host_session_id": host_sid,
65
+ "source": host,
66
+ "start_source": start_source,
67
+ })
68
+ except Exception as exc:
69
+ log_hook_error("hook_session_open", exc)
70
+ try:
71
+ from services.notifications import notify
72
+ notify(project, agent="session", severity="info",
73
+ title=f"Session started {short_id(host_sid)}",
74
+ message=(f"{host} session {host_sid or '?'} started"
75
+ + (f" ({start_source})" if start_source else "")),
76
+ kind="session", ref_id=host_sid)
77
+ except Exception:
78
+ pass
79
+ return None
80
+
81
+
82
+ def main() -> None:
83
+ import json
84
+ try:
85
+ data = json.load(sys.stdin)
86
+ except Exception as exc:
87
+ log_hook_error("hook_session_open", exc)
88
+ sys.exit(0)
89
+ try:
90
+ run(data)
91
+ except Exception as exc:
92
+ log_hook_error("hook_session_open", exc)
93
+ sys.exit(0)
94
+
95
+
96
+ if __name__ == "__main__":
97
+ main()
@@ -3215,6 +3215,43 @@ def api_hub_overrides():
3215
3215
  "limit": limit})
3216
3216
 
3217
3217
 
3218
+ @app.route("/api/hub/overrides/costs", methods=["GET"])
3219
+ def api_hub_override_costs():
3220
+ """Per-(project, rule) request counts over a trailing window (§11 T5).
3221
+
3222
+ The "this rule is costing you" strip. Query: path (optional — omit for
3223
+ every project in the store; the hub is this machine's own surface and
3224
+ does not filter to a registry), days (1..30, default 7). Response:
3225
+ {days, threshold, rules: [...], count, project} with the row shape of
3226
+ ``services.override_costs.FIELDS``; ``threshold`` is the count at which
3227
+ the UI says something, so the strip and the desktop tray agree on it.
3228
+ Registered before the POST-only ``<request_id>`` route so a GET on
3229
+ ``costs`` can never fall through to a 405 there.
3230
+ """
3231
+ from services import override_costs as ocost
3232
+ project_path = ""
3233
+ raw_project = (request.args.get("path") or "").strip()
3234
+ if raw_project:
3235
+ try:
3236
+ project_path = str(_resolve_project_path(raw_project))
3237
+ except ValueError as e:
3238
+ return jsonify({"error": str(e)}), 404
3239
+ try:
3240
+ days = int(request.args.get("days") or ocost.DEFAULT_DAYS)
3241
+ except (TypeError, ValueError):
3242
+ days = ocost.DEFAULT_DAYS
3243
+ days = max(1, min(ocost.MAX_DAYS, days))
3244
+ try:
3245
+ rules = ocost.rule_costs(project_path=project_path or None, days=days)
3246
+ except Exception as exc:
3247
+ return jsonify({"error": str(exc)}), 500
3248
+ for r in rules:
3249
+ r["project_name"] = Path(str(r.get("project_path") or ".")).name
3250
+ return jsonify({"days": days, "threshold": ocost.NUDGE_THRESHOLD,
3251
+ "rules": rules, "count": len(rules),
3252
+ "project": project_path})
3253
+
3254
+
3218
3255
  @app.route("/api/hub/overrides/<request_id>", methods=["POST"])
3219
3256
  def api_hub_override_decide(request_id):
3220
3257
  """Approve (minting a grant) or deny one request. decided_by='desktop'.
@@ -185,11 +185,79 @@ function AccRequestCard({ row, busy, onDecide, onTypedApprove, onRuleApprove })
185
185
  );
186
186
  }
187
187
 
188
+ // ── "Costing you" — the §11 Threat 5 nudge ─────────────────────────────────
189
+ // One row per rule that held at least `threshold` times in the window. The
190
+ // count is the argument: a rule the user keeps approving is friction, and a
191
+ // rule they keep denying is doing its job against an agent that will not
192
+ // stop asking. Either way the fix is the RULE, not the next card — so the
193
+ // only affordance is "open the rule", which jumps to it in the policy matrix
194
+ // below. The strip never approves anything and never edits policy; it says
195
+ // the number out loud, which is the mitigation the spec asks for.
196
+ function accSamePath(a, b) {
197
+ const norm = (p) => String(p || '').replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase();
198
+ return norm(a) === norm(b);
199
+ }
200
+
201
+ function AccCostStrip({ onOpenRule }) {
202
+ const [data, setData] = useState(null);
203
+
204
+ const load = useCallback(async () => {
205
+ try {
206
+ setData(await api.get('/api/hub/overrides/costs?days=7'));
207
+ } catch { /* keep the last good strip */ }
208
+ }, []);
209
+
210
+ useEffect(() => { load(); }, [load]);
211
+ usePoll(load, 15000);
212
+
213
+ if (!data) return null;
214
+ const threshold = Number(data.threshold) || 3;
215
+ const rows = (data.rules || []).filter(r => (Number(r.count) || 0) >= threshold);
216
+ if (rows.length === 0) return null;
217
+ return (
218
+ <div style={{
219
+ background: T.surface, border: `1px solid ${T.warn}`, borderRadius: 10,
220
+ padding: '10px 14px',
221
+ }}>
222
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 4 }}>
223
+ <span style={{ fontSize: 12.5, fontWeight: 700, color: T.warn }}>Costing you</span>
224
+ <span style={{ fontSize: 11, color: T.textDim }}>
225
+ rules that held {threshold}+ times in the last {data.days} days — fix the
226
+ rule, not the next card
227
+ </span>
228
+ </div>
229
+ {rows.map(r => (
230
+ <div key={`${r.project_path}|${r.rule}`} style={{
231
+ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap',
232
+ padding: '4px 0', fontSize: 11.5, color: T.textMuted,
233
+ }}>
234
+ <span className="mono" title={r.project_path} style={{
235
+ fontSize: 10.5, padding: '2px 7px', borderRadius: 4,
236
+ background: T.surfaceAlt, color: T.textMuted,
237
+ }}>{r.project_name || r.project_path}</span>
238
+ <span>
239
+ <b className="mono" style={{ color: T.text }}>{r.rule}</b>
240
+ {' '}held {r.count}× this week ({r.approved} approved) — {r.suggestion}
241
+ </span>
242
+ <div style={{ flex: 1 }} />
243
+ <Btn variant="ghost" onClick={() => onOpenRule(r)}
244
+ style={{ padding: '4px 10px' }}>Open rule</Btn>
245
+ </div>
246
+ ))}
247
+ </div>
248
+ );
249
+ }
250
+
188
251
  // ── Read-only per-project policy matrix ────────────────────────────────────
189
- function AccRulesPanel({ projects }) {
252
+ // `focus` ({path, rule, seq}) comes from the cost strip's "Open rule": the
253
+ // panel switches to that project, scrolls itself into view and outlines the
254
+ // chip whose glob is the rule. A synthetic rule (discipline / shell) has no
255
+ // chip, so the scroll alone is the answer there.
256
+ function AccRulesPanel({ projects, focus }) {
190
257
  const [path, setPath] = useState('');
191
258
  const [data, setData] = useState(null);
192
259
  const [err, setErr] = useState('');
260
+ const panelRef = useRef(null);
193
261
 
194
262
  const load = useCallback(async (p) => {
195
263
  if (!p) { setData(null); setErr(''); return; }
@@ -199,10 +267,28 @@ function AccRulesPanel({ projects }) {
199
267
  } catch (e) { setData(null); setErr(apiErr(e)); }
200
268
  }, []);
201
269
 
270
+ // Deliberately keyed on `focus` alone: `projects` is rebuilt by the hub's
271
+ // own poll, and re-running this on every poll would re-scroll the page
272
+ // under the user every few seconds.
273
+ useEffect(() => {
274
+ if (!focus || !focus.path) return;
275
+ // Prefer the hub's own spelling of the project so the <select> shows it;
276
+ // the store's spelling still loads when the project is not registered.
277
+ const known = (projects || []).find(p => accSamePath(p.path, focus.path));
278
+ const target = known ? known.path : focus.path;
279
+ setPath(target);
280
+ load(target);
281
+ const el = panelRef.current;
282
+ if (el && typeof el.scrollIntoView === 'function') {
283
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' });
284
+ }
285
+ }, [focus]); // eslint-disable-line react-hooks/exhaustive-deps
286
+
287
+ const hot = focus && focus.rule ? String(focus.rule) : '';
202
288
  const scopes = (data && data.rules) || {};
203
289
  const layerRows = data && data.policy ? Object.entries(data.policy.layers || {}) : [];
204
290
  return (
205
- <div style={{
291
+ <div ref={panelRef} style={{
206
292
  background: T.surface, border: `1px solid ${T.border}`, borderRadius: 10,
207
293
  padding: '12px 16px',
208
294
  }}>
@@ -249,14 +335,21 @@ function AccRulesPanel({ projects }) {
249
335
  <div style={{ fontSize: 11.5, color: T.textDim }}>no rules</div>
250
336
  ) : (
251
337
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
252
- {rows.map(([kind, glob], i) => (
253
- <span key={i} className="mono" style={{
254
- fontSize: 11, padding: '2px 8px', borderRadius: 4,
255
- color: ACC_KIND_COLOR(kind),
256
- background: `${ACC_KIND_COLOR(kind)}18`,
257
- border: `1px solid ${ACC_KIND_COLOR(kind)}44`,
258
- }} title={kind}>{kind}: {glob}</span>
259
- ))}
338
+ {rows.map(([kind, glob], i) => {
339
+ const lit = hot && glob === hot;
340
+ return (
341
+ <span key={i} className="mono" style={{
342
+ fontSize: 11, padding: '2px 8px', borderRadius: 4,
343
+ color: ACC_KIND_COLOR(kind),
344
+ background: `${ACC_KIND_COLOR(kind)}18`,
345
+ border: `1px solid ${ACC_KIND_COLOR(kind)}44`,
346
+ outline: lit ? `2px solid ${T.warn}` : 'none',
347
+ outlineOffset: lit ? 2 : 0,
348
+ }} title={lit ? `${kind} — this rule is costing you` : kind}>
349
+ {kind}: {glob}
350
+ </span>
351
+ );
352
+ })}
260
353
  </div>
261
354
  )}
262
355
  {scope === 'builtin' && (sec.disabled || []).length > 0 && (
@@ -395,6 +488,11 @@ function HubAccess({ projects }) {
395
488
  const [chip, setChip] = useState('pending');
396
489
  const [busyId, setBusyId] = useState('');
397
490
  const [confirmSpec, setConfirmSpec] = useState(null);
491
+ // `seq` makes a second click on the same rule re-fire the panel's effect.
492
+ const [ruleFocus, setRuleFocus] = useState(null);
493
+ const openRule = (r) => setRuleFocus({
494
+ path: r.project_path, rule: r.rule, seq: Date.now(),
495
+ });
398
496
 
399
497
  const load = useCallback(async () => {
400
498
  try {
@@ -459,6 +557,8 @@ function HubAccess({ projects }) {
459
557
  ))}
460
558
  </div>
461
559
 
560
+ <AccCostStrip onOpenRule={openRule} />
561
+
462
562
  {!loaded ? (
463
563
  <div style={{ fontSize: 12, color: T.textDim }}>Loading…</div>
464
564
  ) : rows.length === 0 ? (
@@ -479,7 +579,7 @@ function HubAccess({ projects }) {
479
579
  )}
480
580
 
481
581
  <AccGrantsPanel projects={projects} />
482
- <AccRulesPanel projects={projects} />
582
+ <AccRulesPanel projects={projects} focus={ruleFocus} />
483
583
  {confirmSpec && (
484
584
  <CredConfirm spec={confirmSpec} onClose={() => setConfirmSpec(null)} />
485
585
  )}