code-context-control 2.127.0__tar.gz → 2.128.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 (532) hide show
  1. {code_context_control-2.127.0 → code_context_control-2.128.1}/PKG-INFO +3 -1
  2. {code_context_control-2.127.0 → code_context_control-2.128.1}/README.md +2 -0
  3. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/_hook_utils.py +22 -0
  4. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/c3.py +5 -6
  5. code_context_control-2.128.1/cli/hook_session_open.py +199 -0
  6. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_server.py +195 -49
  7. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/project_card.js +6 -1
  8. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/mcp_server.py +36 -0
  9. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/server.py +7 -0
  10. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/PKG-INFO +3 -1
  11. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/SOURCES.txt +4 -0
  12. {code_context_control-2.127.0 → code_context_control-2.128.1}/pyproject.toml +1 -1
  13. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/activity_log.py +48 -0
  14. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/project_manager.py +263 -25
  15. code_context_control-2.128.1/services/session_live.py +139 -0
  16. code_context_control-2.128.1/tests/test_activity_log_find_last.py +96 -0
  17. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_session_end.py +125 -0
  18. code_context_control-2.128.1/tests/test_hub_hook_migration.py +230 -0
  19. code_context_control-2.128.1/tests/test_project_manager.py +374 -0
  20. code_context_control-2.128.1/tests/test_session_live.py +142 -0
  21. code_context_control-2.127.0/cli/hook_session_open.py +0 -97
  22. code_context_control-2.127.0/tests/test_project_manager.py +0 -134
  23. {code_context_control-2.127.0 → code_context_control-2.128.1}/LICENSE +0 -0
  24. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/__init__.py +0 -0
  25. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/_shell_writes.py +0 -0
  26. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/commands/__init__.py +0 -0
  27. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/commands/common.py +0 -0
  28. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/commands/parser.py +0 -0
  29. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/docs.html +0 -0
  30. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/edits.html +0 -0
  31. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/access.html +0 -0
  32. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/bitbucket.html +0 -0
  33. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/credentials.html +0 -0
  34. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/getting-started.html +0 -0
  35. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/index.html +0 -0
  36. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/masking.html +0 -0
  37. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/oracle.html +0 -0
  38. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/shared.css +0 -0
  39. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/tools.html +0 -0
  40. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/guide/workflow.html +0 -0
  41. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_access_guard.py +0 -0
  42. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_artifact.py +0 -0
  43. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_auto_snapshot.py +0 -0
  44. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_c3_signal.py +0 -0
  45. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_c3read.py +0 -0
  46. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_codex_lifecycle.py +0 -0
  47. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_dispatch.py +0 -0
  48. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_edit_ledger.py +0 -0
  49. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_edit_unlock.py +0 -0
  50. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_filter.py +0 -0
  51. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_ghost_files.py +0 -0
  52. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_pretool_enforce.py +0 -0
  53. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_prompt_recall.py +0 -0
  54. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_read.py +0 -0
  55. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_session_end.py +0 -0
  56. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_session_stats.py +0 -0
  57. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hook_terse_advisor.py +0 -0
  58. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/app.js +0 -0
  59. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/add_project.js +0 -0
  60. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/config_editor.js +0 -0
  61. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
  62. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_health.js +0 -0
  63. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_panel.js +0 -0
  64. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_subprojects.js +0 -0
  65. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_tasks.js +0 -0
  66. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/drill_views.js +0 -0
  67. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/global_search.js +0 -0
  68. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_access.js +0 -0
  69. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_ci.js +0 -0
  70. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  71. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_credentials.js +0 -0
  72. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_enforcement.js +0 -0
  73. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_locks.js +0 -0
  74. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/hub_tokens.js +0 -0
  75. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/mcp_manager.js +0 -0
  76. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/modals.js +0 -0
  77. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/project_tree.js +0 -0
  78. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/session_drawer.js +0 -0
  79. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/settings_modal.js +0 -0
  80. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/sidebar.js +0 -0
  81. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/summary_bar.js +0 -0
  82. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/task_board.js +0 -0
  83. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/toasts.js +0 -0
  84. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/components/topbar.js +0 -0
  85. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui/state.js +0 -0
  86. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/hub_ui.html +0 -0
  87. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/mcp_proxy.py +0 -0
  88. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/progress.py +0 -0
  89. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/__init__.py +0 -0
  90. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/_grants.py +0 -0
  91. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/_helpers.py +0 -0
  92. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/agent.py +0 -0
  93. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/artifacts.py +0 -0
  94. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/bitbucket.py +0 -0
  95. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/ci.py +0 -0
  96. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/compress.py +0 -0
  97. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/credentials.py +0 -0
  98. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/delegate.py +0 -0
  99. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/edit.py +0 -0
  100. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/edit_verify.py +0 -0
  101. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/edits.py +0 -0
  102. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/federate.py +0 -0
  103. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/filter.py +0 -0
  104. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/impact.py +0 -0
  105. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/jira.py +0 -0
  106. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/locks.py +0 -0
  107. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/memory.py +0 -0
  108. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/override.py +0 -0
  109. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/project.py +0 -0
  110. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/read.py +0 -0
  111. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/search.py +0 -0
  112. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/session.py +0 -0
  113. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/shell.py +0 -0
  114. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/shell_job.py +0 -0
  115. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/shell_nudge.py +0 -0
  116. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/shell_parsers.py +0 -0
  117. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/shell_render.py +0 -0
  118. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/status.py +0 -0
  119. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/tasks.py +0 -0
  120. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/tools/validate.py +0 -0
  121. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/api.js +0 -0
  122. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/app.js +0 -0
  123. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/access.js +0 -0
  124. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/bitbucket.js +0 -0
  125. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/chat.js +0 -0
  126. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/credentials.js +0 -0
  127. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/dashboard.js +0 -0
  128. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/edits.js +0 -0
  129. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/enforcement.js +0 -0
  130. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/instructions.js +0 -0
  131. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/jira.js +0 -0
  132. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/memory.js +0 -0
  133. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/sessions.js +0 -0
  134. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/settings.js +0 -0
  135. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/sidebar.js +0 -0
  136. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/components/tasks.js +0 -0
  137. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/icons.js +0 -0
  138. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/pm_shared.js +0 -0
  139. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/shared.js +0 -0
  140. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui/theme.js +0 -0
  141. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui.html +0 -0
  142. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui_legacy.html +0 -0
  143. {code_context_control-2.127.0 → code_context_control-2.128.1}/cli/ui_nano.html +0 -0
  144. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/dependency_links.txt +0 -0
  145. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/entry_points.txt +0 -0
  146. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/requires.txt +0 -0
  147. {code_context_control-2.127.0 → code_context_control-2.128.1}/code_context_control.egg-info/top_level.txt +0 -0
  148. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/__init__.py +0 -0
  149. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/codex_patch.py +0 -0
  150. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/config.py +0 -0
  151. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/host.py +0 -0
  152. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/ide.py +0 -0
  153. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/mcp_toml.py +0 -0
  154. {code_context_control-2.127.0 → code_context_control-2.128.1}/core/web_security.py +0 -0
  155. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/__init__.py +0 -0
  156. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/config.py +0 -0
  157. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/listeners.py +0 -0
  158. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/mcp_oracle.py +0 -0
  159. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/oracle_server.py +0 -0
  160. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/oracle_ui.html +0 -0
  161. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/__init__.py +0 -0
  162. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/activity_reporter.py +0 -0
  163. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/api_auth.py +0 -0
  164. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/c3_bridge.py +0 -0
  165. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/chat_engine.py +0 -0
  166. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/chat_poll.py +0 -0
  167. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/chat_store.py +0 -0
  168. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/client_tokens.py +0 -0
  169. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/cross_memory.py +0 -0
  170. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/discovery_audit.py +0 -0
  171. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/federated_graph.py +0 -0
  172. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/health_checker.py +0 -0
  173. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/insight_engine.py +0 -0
  174. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/local_session.py +0 -0
  175. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/memory_reader.py +0 -0
  176. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/memory_writer.py +0 -0
  177. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/mobile_api.py +0 -0
  178. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/project_scanner.py +0 -0
  179. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/review_agent.py +0 -0
  180. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/tool_executor.py +0 -0
  181. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/services/tool_registry.py +0 -0
  182. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/activity.js +0 -0
  183. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/agents.js +0 -0
  184. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/app.js +0 -0
  185. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/busy.js +0 -0
  186. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/conversations.js +0 -0
  187. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/input.js +0 -0
  188. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/markdown.js +0 -0
  189. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/send.js +0 -0
  190. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/stream_renderer.js +0 -0
  191. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/chat/toolbar.js +0 -0
  192. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/core.js +0 -0
  193. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/crossgraph.js +0 -0
  194. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/header.js +0 -0
  195. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/insights.js +0 -0
  196. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/projects.js +0 -0
  197. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/qrcode.js +0 -0
  198. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/settings.js +0 -0
  199. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/suggestions.js +0 -0
  200. {code_context_control-2.127.0 → code_context_control-2.128.1}/oracle/ui/theme_tabs.js +0 -0
  201. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/__init__.py +0 -0
  202. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/access_guard.py +0 -0
  203. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/access_telemetry.py +0 -0
  204. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/agent_base.py +0 -0
  205. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/agent_locks.py +0 -0
  206. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/agents.py +0 -0
  207. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/artifact_defs.py +0 -0
  208. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/artifact_store.py +0 -0
  209. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/atomic_json.py +0 -0
  210. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/auto_memory.py +0 -0
  211. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/background_service.py +0 -0
  212. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/__init__.py +0 -0
  213. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/external/__init__.py +0 -0
  214. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/external/aider_polyglot.py +0 -0
  215. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/external/swe_bench.py +0 -0
  216. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/map_eval.py +0 -0
  217. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/search_eval.py +0 -0
  218. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bench/shell_eval.py +0 -0
  219. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/benchmark_dashboard.py +0 -0
  220. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bitbucket_client.py +0 -0
  221. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/bitbucket_credentials.py +0 -0
  222. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_act.py +0 -0
  223. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_cache.py +0 -0
  224. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_expr.py +0 -0
  225. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_failures.py +0 -0
  226. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_github.py +0 -0
  227. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_impact.py +0 -0
  228. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_intel.py +0 -0
  229. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_runner.py +0 -0
  230. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ci_workflow.py +0 -0
  231. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/circuit_breaker.py +0 -0
  232. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/claude_md.py +0 -0
  233. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/codex_integration.py +0 -0
  234. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/codex_transcript.py +0 -0
  235. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/compressor.py +0 -0
  236. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/context_snapshot.py +0 -0
  237. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/conversation_store.py +0 -0
  238. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/cred_audit.py +0 -0
  239. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/cred_telemetry.py +0 -0
  240. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/credential_store.py +0 -0
  241. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/dir_map.py +0 -0
  242. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/doc_index.py +0 -0
  243. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/e2e_benchmark.py +0 -0
  244. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/e2e_evaluator.py +0 -0
  245. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/e2e_tasks.py +0 -0
  246. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/edit_ledger.py +0 -0
  247. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/embedding_index.py +0 -0
  248. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/enforcement_policy.py +0 -0
  249. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/error_reporting.py +0 -0
  250. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/file_map.py +0 -0
  251. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/file_memory.py +0 -0
  252. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/git_context.py +0 -0
  253. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/host_sessions.py +0 -0
  254. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/hub_service.py +0 -0
  255. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/index_store.py +0 -0
  256. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/indexer.py +0 -0
  257. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/jira_client.py +0 -0
  258. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/jira_cloud.py +0 -0
  259. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/jira_credentials.py +0 -0
  260. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/jira_data_center.py +0 -0
  261. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/jira_links.py +0 -0
  262. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/lexical_index.py +0 -0
  263. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/mask_activation.py +0 -0
  264. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/mask_mirror.py +0 -0
  265. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/mask_presets.py +0 -0
  266. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory.py +0 -0
  267. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_consolidator.py +0 -0
  268. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_distiller.py +0 -0
  269. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_graph.py +0 -0
  270. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_grounder.py +0 -0
  271. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_queue.py +0 -0
  272. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/memory_scorer.py +0 -0
  273. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/metrics.py +0 -0
  274. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/notifications.py +0 -0
  275. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ollama_bridge.py +0 -0
  276. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ollama_client.py +0 -0
  277. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/ollama_credentials.py +0 -0
  278. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/oracle_service.py +0 -0
  279. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/output_filter.py +0 -0
  280. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/override_costs.py +0 -0
  281. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/override_grants.py +0 -0
  282. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/override_policy.py +0 -0
  283. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/override_requests.py +0 -0
  284. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/override_wake.py +0 -0
  285. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/parser.py +0 -0
  286. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/project_runtime.py +0 -0
  287. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/protocol.py +0 -0
  288. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/proxy_state.py +0 -0
  289. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/repo_map.py +0 -0
  290. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/repo_shape.py +0 -0
  291. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/reranker.py +0 -0
  292. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/retention.py +0 -0
  293. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/retrieval.py +0 -0
  294. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/retrieval_broker.py +0 -0
  295. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/router.py +0 -0
  296. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/runtime.py +0 -0
  297. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/scanner.py +0 -0
  298. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/session_benchmark.py +0 -0
  299. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/session_manager.py +0 -0
  300. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/session_preloader.py +0 -0
  301. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/shell_jobs.py +0 -0
  302. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/shell_output.py +0 -0
  303. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/subprojects.py +0 -0
  304. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/task_store.py +0 -0
  305. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/telemetry.py +0 -0
  306. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/text_index.py +0 -0
  307. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/time_tracker.py +0 -0
  308. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/tool_classifier.py +0 -0
  309. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/transcript_index.py +0 -0
  310. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/validation_cache.py +0 -0
  311. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/vector_store.py +0 -0
  312. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/watcher.py +0 -0
  313. {code_context_control-2.127.0 → code_context_control-2.128.1}/services/win_subprocess.py +0 -0
  314. {code_context_control-2.127.0 → code_context_control-2.128.1}/setup.cfg +0 -0
  315. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_builtin_optout.py +0 -0
  316. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_guard.py +0 -0
  317. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_guard_meta.py +0 -0
  318. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_guard_shell_project.py +0 -0
  319. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_guard_surfaces.py +0 -0
  320. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_access_guard_wiring.py +0 -0
  321. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_activity_reporter.py +0 -0
  322. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_agent_locks.py +0 -0
  323. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_aider_polyglot.py +0 -0
  324. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_antigravity_transition.py +0 -0
  325. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_artifact_store.py +0 -0
  326. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_artifact_tool.py +0 -0
  327. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_atomic_json.py +0 -0
  328. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_background_service.py +0 -0
  329. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_bitbucket_cli_smoke.py +0 -0
  330. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_bitbucket_client.py +0 -0
  331. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_bitbucket_config_fallback.py +0 -0
  332. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_bitbucket_credentials.py +0 -0
  333. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_bitbucket_tool.py +0 -0
  334. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_builtin_modes.py +0 -0
  335. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_c3_bridge_project_artifacts.py +0 -0
  336. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_c3_shell.py +0 -0
  337. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_chat_poll.py +0 -0
  338. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_act.py +0 -0
  339. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_cache.py +0 -0
  340. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_expr.py +0 -0
  341. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_impact.py +0 -0
  342. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_intel.py +0 -0
  343. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_runner.py +0 -0
  344. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ci_workflow.py +0 -0
  345. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_circuit_breaker.py +0 -0
  346. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_claude_md_merge.py +0 -0
  347. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_claude_md_updater.py +0 -0
  348. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_cli_smoke.py +0 -0
  349. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_codex_mcp_transport.py +0 -0
  350. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_codex_native.py +0 -0
  351. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_compressor_large_file.py +0 -0
  352. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_confirm_guard.py +0 -0
  353. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_confirm_guard_wiring.py +0 -0
  354. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_cooccurrence_bounds.py +0 -0
  355. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_cred_audit.py +0 -0
  356. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_cred_telemetry.py +0 -0
  357. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_credential_login_kind.py +0 -0
  358. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_credential_store.py +0 -0
  359. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_credential_ui_parity.py +0 -0
  360. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_credentials_routes.py +0 -0
  361. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_credentials_tool.py +0 -0
  362. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_creds_cli_smoke.py +0 -0
  363. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_delegate_cascade.py +0 -0
  364. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_delegate_version_probe.py +0 -0
  365. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_dir_map_c3.py +0 -0
  366. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_e2e_benchmark.py +0 -0
  367. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_edit_ledger_deadline.py +0 -0
  368. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_edit_ledger_hook.py +0 -0
  369. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_edit_locking.py +0 -0
  370. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_edit_normalization.py +0 -0
  371. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_edit_verify.py +0 -0
  372. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_embedding_index_deadlock.py +0 -0
  373. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_enforcement_flip.py +0 -0
  374. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_enforcement_policy.py +0 -0
  375. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_enforcement_routes.py +0 -0
  376. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_env_parser.py +0 -0
  377. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_failed_calls_do_not_unlock.py +0 -0
  378. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_federated_graph.py +0 -0
  379. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_file_map_c1.py +0 -0
  380. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_filter_backoff.py +0 -0
  381. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ghost_files.py +0 -0
  382. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ghost_generation.py +0 -0
  383. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_git_branch_awareness.py +0 -0
  384. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_access_guard.py +0 -0
  385. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_codex_compat.py +0 -0
  386. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_dispatch.py +0 -0
  387. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_pretool_enforce.py +0 -0
  388. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_prompt_recall.py +0 -0
  389. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_smoke.py +0 -0
  390. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_state.py +0 -0
  391. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hook_utf8_stdio.py +0 -0
  392. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_credentials_route.py +0 -0
  393. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_credentials_routes_write.py +0 -0
  394. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_enforcement_routes.py +0 -0
  395. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_inspect_api.py +0 -0
  396. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_locks_routes.py +0 -0
  397. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_oracle_service_routes.py +0 -0
  398. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_override_routes.py +0 -0
  399. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_server_smoke.py +0 -0
  400. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_hub_subproject_links.py +0 -0
  401. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ide_workflow_adaptation.py +0 -0
  402. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_index_cap_not_hardcoded.py +0 -0
  403. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_init_clear_preserves_user_content.py +0 -0
  404. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_init_scan.py +0 -0
  405. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_install_mcp_entrypoint.py +0 -0
  406. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_cli_smoke.py +0 -0
  407. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_client.py +0 -0
  408. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_config_fallback.py +0 -0
  409. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_credentials.py +0 -0
  410. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_links.py +0 -0
  411. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_routes.py +0 -0
  412. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_jira_tool.py +0 -0
  413. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_large_files_c4.py +0 -0
  414. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_lazy_store_init.py +0 -0
  415. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_map_eval.py +0 -0
  416. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_map_telemetry_c0.py +0 -0
  417. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mask_guard.py +0 -0
  418. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mask_routes.py +0 -0
  419. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mask_surfaces.py +0 -0
  420. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mcp_host_guard.py +0 -0
  421. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mcp_memory_timeout.py +0 -0
  422. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mcp_server_smoke.py +0 -0
  423. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mcp_toml.py +0 -0
  424. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_memory_distiller.py +0 -0
  425. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_memory_graph_api.py +0 -0
  426. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_memory_queue.py +0 -0
  427. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_memory_system.py +0 -0
  428. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_api.py +0 -0
  429. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_clients.py +0 -0
  430. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_extras.py +0 -0
  431. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_jobs_ci.py +0 -0
  432. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_override_costs.py +0 -0
  433. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_override_routes.py +0 -0
  434. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_mobile_security_api.py +0 -0
  435. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_no_stale_model_pins.py +0 -0
  436. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_notification_dedup.py +0 -0
  437. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_notification_discipline.py +0 -0
  438. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_notification_kinds.py +0 -0
  439. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ollama_credentials.py +0 -0
  440. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_api_auth.py +0 -0
  441. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_apikey_api.py +0 -0
  442. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_c3_bridge.py +0 -0
  443. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_chat_engine.py +0 -0
  444. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_chat_store.py +0 -0
  445. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_discovery_api.py +0 -0
  446. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_discovery_audit.py +0 -0
  447. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_health_probe.py +0 -0
  448. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_local_auth.py +0 -0
  449. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_loopback.py +0 -0
  450. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_ollama_bridge.py +0 -0
  451. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_scanner_cache.py +0 -0
  452. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_security_fixes.py +0 -0
  453. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_session_bootstrap.py +0 -0
  454. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_subproject_awareness.py +0 -0
  455. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_oracle_ui_bundle.py +0 -0
  456. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_output_filter.py +0 -0
  457. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_override_costs.py +0 -0
  458. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_override_grants.py +0 -0
  459. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_override_grants_mcp.py +0 -0
  460. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_override_requests.py +0 -0
  461. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_override_wake.py +0 -0
  462. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_permissions.py +0 -0
  463. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_pm_api.py +0 -0
  464. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_project_manager_merge.py +0 -0
  465. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_project_tool.py +0 -0
  466. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_read_coercion.py +0 -0
  467. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_read_edit_parity.py +0 -0
  468. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_related_facts.py +0 -0
  469. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_repo_map.py +0 -0
  470. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_repo_shape.py +0 -0
  471. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_response_boilerplate.py +0 -0
  472. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_retention.py +0 -0
  473. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_review_digest.py +0 -0
  474. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_eval.py +0 -0
  475. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p1.py +0 -0
  476. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p2.py +0 -0
  477. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p2b.py +0 -0
  478. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p3.py +0 -0
  479. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p4.py +0 -0
  480. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_search_p5.py +0 -0
  481. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_service_durability.py +0 -0
  482. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_session_benchmark.py +0 -0
  483. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_session_budget.py +0 -0
  484. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_budget_s1.py +0 -0
  485. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_creds.py +0 -0
  486. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_eval.py +0 -0
  487. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_jobs_s3.py +0 -0
  488. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_nudge_s4.py +0 -0
  489. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_output.py +0 -0
  490. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_parsers_s2.py +0 -0
  491. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_robustness.py +0 -0
  492. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_telemetry_s0.py +0 -0
  493. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_write_ledger.py +0 -0
  494. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_shell_writes.py +0 -0
  495. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subproject_clear_scope.py +0 -0
  496. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subproject_depth_consumers.py +0 -0
  497. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subproject_exclusion.py +0 -0
  498. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subproject_federation.py +0 -0
  499. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subproject_linkage_api.py +0 -0
  500. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_subprojects.py +0 -0
  501. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_swe_bench.py +0 -0
  502. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_task_store.py +0 -0
  503. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_task_tool.py +0 -0
  504. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_time_tracker.py +0 -0
  505. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_token_telemetry.py +0 -0
  506. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_token_tracking.py +0 -0
  507. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_tool_registry.py +0 -0
  508. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_ui_jsx_syntax.py +0 -0
  509. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_upgrade_and_version.py +0 -0
  510. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_validate.py +0 -0
  511. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_vault_write_guard.py +0 -0
  512. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_version_sync.py +0 -0
  513. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_web_security.py +0 -0
  514. {code_context_control-2.127.0 → code_context_control-2.128.1}/tests/test_windows_reliability.py +0 -0
  515. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/__init__.py +0 -0
  516. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/backend.py +0 -0
  517. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/main.py +0 -0
  518. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/__init__.py +0 -0
  519. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/benchmark_view.py +0 -0
  520. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/claudemd_view.py +0 -0
  521. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/compress_view.py +0 -0
  522. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/index_view.py +0 -0
  523. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/init_view.py +0 -0
  524. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/mcp_view.py +0 -0
  525. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/optimize_view.py +0 -0
  526. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/pipe_view.py +0 -0
  527. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/projects_view.py +0 -0
  528. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/search_view.py +0 -0
  529. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/session_view.py +0 -0
  530. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/stats.py +0 -0
  531. {code_context_control-2.127.0 → code_context_control-2.128.1}/tui/screens/ui_view.py +0 -0
  532. {code_context_control-2.127.0 → code_context_control-2.128.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.127.0
3
+ Version: 2.128.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
@@ -168,6 +168,8 @@ c3 ui # binds the first free port from 3333
168
168
 
169
169
  Twelve tabs. The dashboard above shows token savings, indexed files, the live session, and a stream of recent tool calls and file changes.
170
170
 
171
+ Since v2.128.0 you rarely start it by hand: opening an IDE session in a registered project brings its UI up, and a session proves it is alive with a heartbeat rather than with tool-call traffic — so a quiet session stays live and a closed one disappears at once. Servers a session started are reaped once the session is gone; the ones you started are not. → [Session liveness](https://github.com/drknowhow/code-context-control/blob/main/docs/session-liveness.md)
172
+
171
173
  | Tab | What it's for |
172
174
  |---|---|
173
175
  | **Dashboard** | Token savings, codebase breakdown, live session counters, recent activity |
@@ -102,6 +102,8 @@ c3 ui # binds the first free port from 3333
102
102
 
103
103
  Twelve tabs. The dashboard above shows token savings, indexed files, the live session, and a stream of recent tool calls and file changes.
104
104
 
105
+ Since v2.128.0 you rarely start it by hand: opening an IDE session in a registered project brings its UI up, and a session proves it is alive with a heartbeat rather than with tool-call traffic — so a quiet session stays live and a closed one disappears at once. Servers a session started are reaped once the session is gone; the ones you started are not. → [Session liveness](https://github.com/drknowhow/code-context-control/blob/main/docs/session-liveness.md)
106
+
105
107
  | Tab | What it's for |
106
108
  |---|---|
107
109
  | **Dashboard** | Token savings, codebase breakdown, live session counters, recent activity |
@@ -8,6 +8,7 @@ All hook reads/writes of enforcement state MUST go through this module.
8
8
  import json
9
9
  import os
10
10
  import re
11
+ import shlex
11
12
  import sys
12
13
  import traceback
13
14
  from datetime import datetime, timezone
@@ -34,6 +35,27 @@ LEGACY_UNLOCK_FILE = ".c3/unlocked_files.json"
34
35
  STATE_WARNINGS: list = []
35
36
 
36
37
 
38
+ def hook_command_arg(raw) -> str:
39
+ """Quote one argv element for a hook ``command`` string in settings.
40
+
41
+ Windows: a DOUBLE-quoted forward-slash path. Claude Code runs hooks through
42
+ Git Bash, where ``shlex.quote`` produces SINGLE quotes around a backslash
43
+ path — cmd.exe does not treat single quotes as quoting, so any path with a
44
+ space or parentheses (``Claude Code Companion (C3)``) breaks. A
45
+ double-quoted forward-slash path is parsed correctly by bash AND cmd.
46
+ Verified 2026-07-26 under Git Bash on Windows 11; the installer in
47
+ cli/c3.py carries the full investigation, including why no ``cmd.exe /c``
48
+ wrapper belongs here.
49
+
50
+ Anything that writes a hook command — the installer and the hub's
51
+ migration — must go through this, or the entry it writes is dead on
52
+ Windows.
53
+ """
54
+ if sys.platform == "win32":
55
+ return '"' + str(raw).replace("\\", "/") + '"'
56
+ return shlex.quote(str(raw))
57
+
58
+
37
59
  def ensure_utf8_stdio() -> None:
38
60
  """Force UTF-8 on the hook process's stdio pipes.
39
61
 
@@ -33,7 +33,6 @@ import json
33
33
  import logging
34
34
  import os
35
35
  import re
36
- import shlex
37
36
  import subprocess
38
37
  import sys
39
38
  import tempfile
@@ -92,7 +91,7 @@ console = Console() if HAS_RICH else None
92
91
  # Config
93
92
  CONFIG_DIR = ".c3"
94
93
  CONFIG_FILE = ".c3/config.json"
95
- __version__ = "2.127.0"
94
+ __version__ = "2.128.1"
96
95
 
97
96
 
98
97
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -5542,10 +5541,10 @@ def cmd_install_mcp(args):
5542
5541
  # the wrapper was never needed. A double-quoted forward-slash path is parsed
5543
5542
  # correctly by bash AND cmd, including paths containing spaces or parentheses
5544
5543
  # (e.g. "Claude Code Companion (C3)"), which is why the wrapper was added.
5545
- def _hook_arg(raw: str) -> str:
5546
- if sys.platform == "win32":
5547
- return '"' + str(raw).replace("\\", "/") + '"'
5548
- return shlex.quote(str(raw))
5544
+ # Lives in cli/_hook_utils so the hub's hook migration quotes commands
5545
+ # the same way — it used shlex.quote and wrote entries that Windows
5546
+ # could not run.
5547
+ from cli._hook_utils import hook_command_arg as _hook_arg
5549
5548
 
5550
5549
  # v2.42: single dispatcher script per hook event instead of N separate
5551
5550
  # per-hook commands. One interpreter spawn per event; the dispatcher
@@ -0,0 +1,199 @@
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
+ It also brings up the project's UI server when one is not already running
26
+ (``ensure_ui`` below, v2.128.0) — the only place in C3 where "a session
27
+ started" causes "the UI is up". It needs the project to be registered with
28
+ the hub, and it skips a project that already has a live UI port, so a
29
+ ``resume`` or ``clear`` never stacks a second server. Opt out with
30
+ ``C3_NO_UI_AUTOSTART=1`` or
31
+ ``.c3/config.json → {"ui": {"autostart_on_session": false}}``.
32
+
33
+ ``source: "compact"`` writes NOTHING: a compaction is the same session and
34
+ the same MCP process, and a "Session started" toast on every compaction
35
+ would teach the user to ignore the real ones.
36
+
37
+ The MCP server's own ``session_start`` row is untouched — it carries the C3
38
+ id and, when the host put its id in the environment, ``host_session_id``
39
+ too. This hook is what lets a client see the session before the runtime has
40
+ finished starting (~seconds), and it is the row the desktop's "MCP failed to
41
+ connect" sentinel starts its 30 s clock from.
42
+
43
+ Returns None always: SessionStart hook stdout becomes model context, and
44
+ this hook has nothing to tell the model.
45
+ """
46
+ from __future__ import annotations
47
+
48
+ import json
49
+ import os
50
+ import sys
51
+ from pathlib import Path
52
+
53
+ sys.path.insert(0, str(Path(__file__).parent.parent))
54
+
55
+ from cli._hook_utils import detect_host, find_project, log_hook_error # noqa: E402
56
+ from cli.hook_session_end import short_id # noqa: E402
57
+
58
+ _SILENT_SOURCES = {"compact"}
59
+
60
+
61
+ def _ui_autostart_enabled(project: Path) -> bool:
62
+ """Opt-outs, in the order a user reaches for them."""
63
+ if os.environ.get("C3_NO_UI_AUTOSTART") or os.environ.get("C3_BENCHMARK_MODE"):
64
+ return False
65
+ try:
66
+ cfg = json.loads((project / ".c3" / "config.json").read_text(encoding="utf-8"))
67
+ except Exception:
68
+ return True # no readable config is not a reason to withhold the UI
69
+ ui = cfg.get("ui")
70
+ if isinstance(ui, dict) and "autostart_on_session" in ui:
71
+ return bool(ui.get("autostart_on_session"))
72
+ return True
73
+
74
+
75
+ def _is_registered(pm, project: Path) -> bool:
76
+ """True when the hub knows this project.
77
+
78
+ The UI server is a hub-level surface: a directory the user never
79
+ registered gets nothing automatic (``c3 ui`` still works). This is also
80
+ what keeps a test fixture or a scratch clone from growing a web server.
81
+ """
82
+ target = os.path.normcase(str(project))
83
+ for entry in pm._read_projects():
84
+ raw = str(entry.get("path") or "")
85
+ if raw and os.path.normcase(raw) == target:
86
+ return True
87
+ try:
88
+ if raw and os.path.normcase(str(Path(raw).resolve())) == target:
89
+ return True
90
+ except Exception:
91
+ continue
92
+ return False
93
+
94
+
95
+ def _ui_running(pm, project: Path) -> bool:
96
+ """True when a live UI server is already registered for this project.
97
+
98
+ This is what keeps `resume` / `clear` — both real SessionStart events —
99
+ from stacking a second server on every reconnect.
100
+ """
101
+ target = os.path.normcase(str(project))
102
+ for entry in pm._read_registry():
103
+ raw = str(entry.get("project_path") or "")
104
+ if not raw:
105
+ continue
106
+ try:
107
+ same = os.path.normcase(str(Path(raw).resolve())) == target
108
+ except Exception:
109
+ same = os.path.normcase(raw) == target
110
+ if same and entry.get("port") and pm._port_alive(entry["port"]):
111
+ return True
112
+ return False
113
+
114
+
115
+ def ensure_ui(project: Path, host_session_id: str = "") -> str:
116
+ """Bring up this project's UI server for the session that just opened.
117
+
118
+ Nothing else in C3 connected "a session started" to "the UI is running":
119
+ the hub's sweep only chases projects flagged `autostart_ui`, and before
120
+ 2.128.0 it ran once at hub startup — so a UI was only ever there because
121
+ an earlier session had left one behind.
122
+
123
+ Returns a short reason string (tests and diagnostics read it). Never
124
+ raises and never prints — SessionStart stdout becomes model context.
125
+ """
126
+ try:
127
+ project = Path(project).resolve()
128
+ except Exception:
129
+ project = Path(project)
130
+ if not _ui_autostart_enabled(project):
131
+ return "disabled"
132
+ try:
133
+ from services.project_manager import ProjectManager
134
+ pm = ProjectManager()
135
+ if not _is_registered(pm, project):
136
+ return "unregistered"
137
+ if _ui_running(pm, project):
138
+ return "already-running"
139
+ # launch_session copies os.environ into the detached child, which is
140
+ # where cli/server._register_session reads the ownership from.
141
+ os.environ["C3_UI_OWNER"] = "session"
142
+ if host_session_id:
143
+ os.environ["C3_UI_OWNER_SESSION"] = host_session_id
144
+ result = pm.launch_session(str(project))
145
+ if result.get("launched"):
146
+ return "launched"
147
+ return f"failed: {result.get('error', '')}".strip()
148
+ except Exception as exc:
149
+ log_hook_error("hook_session_open", exc)
150
+ return "error"
151
+
152
+
153
+ def run(payload: dict, project_path: Path | None = None):
154
+ """Core logic — importable by the dispatcher and tests. Returns None."""
155
+ start_source = str(payload.get("source") or "").strip().lower()
156
+ if start_source in _SILENT_SOURCES:
157
+ return None
158
+ project = find_project(payload, project_path)
159
+ if project is None:
160
+ return None
161
+ host = detect_host(payload)
162
+ host_sid = str(payload.get("session_id") or "").strip()
163
+ try:
164
+ from services.activity_log import ActivityLog
165
+ ActivityLog(str(project)).log("session_open", {
166
+ "host_session_id": host_sid,
167
+ "source": host,
168
+ "start_source": start_source,
169
+ })
170
+ except Exception as exc:
171
+ log_hook_error("hook_session_open", exc)
172
+ try:
173
+ from services.notifications import notify
174
+ notify(project, agent="session", severity="info",
175
+ title=f"Session started {short_id(host_sid)}",
176
+ message=(f"{host} session {host_sid or '?'} started"
177
+ + (f" ({start_source})" if start_source else "")),
178
+ kind="session", ref_id=host_sid)
179
+ except Exception:
180
+ pass
181
+ ensure_ui(project, host_sid)
182
+ return None
183
+
184
+
185
+ def main() -> None:
186
+ try:
187
+ data = json.load(sys.stdin)
188
+ except Exception as exc:
189
+ log_hook_error("hook_session_open", exc)
190
+ sys.exit(0)
191
+ try:
192
+ run(data)
193
+ except Exception as exc:
194
+ log_hook_error("hook_session_open", exc)
195
+ sys.exit(0)
196
+
197
+
198
+ if __name__ == "__main__":
199
+ main()
@@ -29,6 +29,7 @@ from flask import Flask, jsonify, redirect, request, send_from_directory
29
29
 
30
30
  sys.path.insert(0, str(Path(__file__).parent.parent))
31
31
 
32
+ from cli._hook_utils import hook_command_arg
32
33
  from core.ide import PROFILES, detect_ide, get_profile, load_ide_config, normalize_ide_name
33
34
  from services import access_guard
34
35
  from services.activity_log import ActivityLog
@@ -71,8 +72,14 @@ _HUB_CONFIG_DEFAULTS = {
71
72
  "projects_view": "list",
72
73
  "main_view": "projects",
73
74
  "oracle_url": "",
75
+ # Minutes of project silence, with no session heartbeat left, before a UI
76
+ # server that an IDE session launched is stopped. 0 = never reap.
77
+ "ui_reap_minutes": 30,
74
78
  }
75
79
 
80
+ # How often the hub reaps orphaned UI servers and chases autostart projects.
81
+ _UI_SWEEP_INTERVAL_S = 60
82
+
76
83
 
77
84
  def _read_hub_config() -> dict:
78
85
  cfg = dict(_HUB_CONFIG_DEFAULTS)
@@ -4373,28 +4380,168 @@ def method_not_allowed(e):
4373
4380
 
4374
4381
  # ─── Hook migration ──────────────────────────────────────────────────────────
4375
4382
 
4376
- def _migrate_project_hooks():
4377
- """Idempotently add new C3 hooks to all registered projects' Claude and Gemini settings.
4383
+ def _has_matcher(value: str):
4384
+ return lambda entries: any(e.get("matcher") == value for e in entries)
4385
+
4386
+
4387
+ def _has_command(fragment: str):
4388
+ return lambda entries: any(
4389
+ fragment in (hk.get("command") or "")
4390
+ for e in entries
4391
+ for hk in e.get("hooks", [])
4392
+ )
4393
+
4394
+
4395
+ def c3_hook_migrations(cli_dir: Path | None = None) -> list:
4396
+ """C3's own hook entries, per settings file and hook event.
4397
+
4398
+ Adding a hook to the installer (``cli/c3.py``) does NOT reach a project
4399
+ that is already installed. The v2.126.0 SessionStart / SessionEnd entries
4400
+ landed in the installer only, so every existing project kept writing no
4401
+ ``session_open`` / ``session_end`` rows at all — 60 projects on the
4402
+ machine this was found on, including C3's own. This table is how they
4403
+ catch up: the hub applies it at startup, additively and idempotently.
4378
4404
 
4379
- Runs at hub startup so that existing projects pick up hook changes
4380
- without requiring a manual 'c3 mcp install' on each project.
4405
+ ``present`` decides "C3's entry for this event is already here", so a
4406
+ re-run is a no-op and a user's own entries for the same event survive.
4381
4407
  """
4382
- cli_dir = Path(__file__).parent
4383
- hook_c3read_cmd = (
4384
- f"{shlex.quote(sys.executable)} "
4385
- f"{shlex.quote(str(cli_dir / 'hook_c3read.py'))}"
4408
+ cli_dir = cli_dir or Path(__file__).parent
4409
+ # hook_command_arg, not shlex.quote: on Windows the latter writes a
4410
+ # single-quoted backslash path that cmd.exe does not unquote, so every
4411
+ # command this table produced for a path with a space — every path under
4412
+ # "1. Projects", say — was dead on arrival. The installer has used the
4413
+ # double-quoted forward-slash form since 2026-07-26; this now matches it.
4414
+ dispatch = (
4415
+ f"{hook_command_arg(sys.executable)} "
4416
+ f"{hook_command_arg(str(cli_dir / 'hook_dispatch.py'))}"
4386
4417
  )
4387
- new_hook = {
4418
+ c3read_entry = {
4388
4419
  "matcher": "mcp__c3__c3_read",
4389
- "hooks": [{"type": "command", "command": hook_c3read_cmd}],
4420
+ "hooks": [{
4421
+ "type": "command",
4422
+ "command": (f"{hook_command_arg(sys.executable)} "
4423
+ f"{hook_command_arg(str(cli_dir / 'hook_c3read.py'))}"),
4424
+ }],
4390
4425
  }
4426
+ c3read_present = _has_matcher("mcp__c3__c3_read")
4427
+ dispatch_present = _has_command("hook_dispatch.py")
4428
+
4429
+ def _lifecycle(event: str, arg: str) -> dict:
4430
+ return {
4431
+ "settings": ".claude/settings.local.json",
4432
+ "event": event,
4433
+ "present": dispatch_present,
4434
+ "entry": {"matcher": "",
4435
+ "hooks": [{"type": "command",
4436
+ "command": f"{dispatch} {arg}"}]},
4437
+ }
4391
4438
 
4392
- # (settings_path, hook_event) pairs to check per project
4393
- _HOOK_TARGETS = [
4394
- (".claude/settings.local.json", "PostToolUse"),
4395
- (".gemini/settings.json", "AfterTool"),
4439
+ return [
4440
+ {"settings": ".claude/settings.local.json", "event": "PostToolUse",
4441
+ "present": c3read_present, "entry": c3read_entry},
4442
+ {"settings": ".gemini/settings.json", "event": "AfterTool",
4443
+ "present": c3read_present, "entry": c3read_entry},
4444
+ # Claude only — Gemini has no session-lifecycle event to hang these on.
4445
+ _lifecycle("SessionStart", "start"),
4446
+ _lifecycle("SessionEnd", "end"),
4396
4447
  ]
4397
4448
 
4449
+
4450
+ def _command_cli_dir(command: str) -> Path | None:
4451
+ """The C3 ``cli/`` directory a hook command points at, if any."""
4452
+ try:
4453
+ tokens = shlex.split(str(command or ""), posix=False)
4454
+ except ValueError:
4455
+ tokens = str(command or "").split()
4456
+ for token in tokens:
4457
+ token = token.strip("'\"")
4458
+ if not token.endswith(".py"):
4459
+ continue
4460
+ candidate = Path(token)
4461
+ if not candidate.name.startswith("hook_"):
4462
+ continue
4463
+ parent = candidate.parent
4464
+ if (parent / "hook_dispatch.py").is_file():
4465
+ return parent
4466
+ return None
4467
+
4468
+
4469
+ def detect_project_cli_dir(path: str) -> Path | None:
4470
+ """The ``cli/`` directory this project's existing C3 hooks already use.
4471
+
4472
+ The hub runs from wherever C3 is installed, so building commands from its
4473
+ own location writes install paths into a project that was installed from a
4474
+ checkout — C3's own repo above all, where the working tree IS the runtime.
4475
+ A hook edited there would then change nothing until the next install. Match
4476
+ what the project already points at; the caller falls back to ours.
4477
+ """
4478
+ for rel_settings in (".claude/settings.local.json", ".gemini/settings.json"):
4479
+ settings_path = Path(path) / rel_settings
4480
+ if not settings_path.exists():
4481
+ continue
4482
+ try:
4483
+ with open(settings_path, encoding="utf-8") as f:
4484
+ hooks = (json.load(f).get("hooks") or {})
4485
+ except Exception:
4486
+ continue
4487
+ for entries in hooks.values():
4488
+ for entry in entries or []:
4489
+ for hook in (entry or {}).get("hooks", []):
4490
+ found = _command_cli_dir((hook or {}).get("command") or "")
4491
+ if found is not None:
4492
+ return found
4493
+ return None
4494
+
4495
+
4496
+ def migrate_hooks_for_project(path: str, migrations: list | None = None) -> int:
4497
+ """Apply the hook table to one project. Returns settings files changed.
4498
+
4499
+ Never creates a settings file that is not already there: a project with no
4500
+ ``.claude/settings.local.json`` has not been installed for Claude, and the
4501
+ hub is not the place to decide that it should be.
4502
+ """
4503
+ if migrations is None:
4504
+ migrations = c3_hook_migrations(detect_project_cli_dir(path))
4505
+ by_file: dict = {}
4506
+ for migration in migrations:
4507
+ by_file.setdefault(migration["settings"], []).append(migration)
4508
+
4509
+ changed_files = 0
4510
+ for rel_settings, items in by_file.items():
4511
+ settings_path = Path(path) / rel_settings
4512
+ if not settings_path.exists():
4513
+ continue
4514
+ try:
4515
+ with open(settings_path, encoding="utf-8") as f:
4516
+ settings = json.load(f)
4517
+ except Exception:
4518
+ continue
4519
+ changed = False
4520
+ for migration in items:
4521
+ event = migration["event"]
4522
+ existing = list(settings.get("hooks", {}).get(event, []))
4523
+ if migration["present"](existing):
4524
+ continue
4525
+ existing.append(migration["entry"])
4526
+ settings.setdefault("hooks", {})[event] = existing
4527
+ changed = True
4528
+ if not changed:
4529
+ continue
4530
+ try:
4531
+ with open(settings_path, "w", encoding="utf-8") as f:
4532
+ json.dump(settings, f, indent=2)
4533
+ changed_files += 1
4534
+ except Exception:
4535
+ pass
4536
+ return changed_files
4537
+
4538
+
4539
+ def _migrate_project_hooks():
4540
+ """Idempotently add missing C3 hooks to every registered project.
4541
+
4542
+ Runs at hub startup so existing projects pick up hook changes without a
4543
+ manual 'c3 install-mcp' per project.
4544
+ """
4398
4545
  try:
4399
4546
  projects = _pm().list_projects()
4400
4547
  except Exception:
@@ -4405,32 +4552,13 @@ def _migrate_project_hooks():
4405
4552
  path = p.get("path", "")
4406
4553
  if not path:
4407
4554
  continue
4408
- for rel_settings, hook_event in _HOOK_TARGETS:
4409
- settings_path = Path(path) / rel_settings
4410
- if not settings_path.exists():
4411
- continue
4412
- try:
4413
- with open(settings_path, encoding="utf-8") as f:
4414
- settings = json.load(f)
4415
- except Exception:
4416
- continue
4417
-
4418
- existing = settings.get("hooks", {}).get(hook_event, [])
4419
- if any(h.get("matcher") == "mcp__c3__c3_read" for h in existing):
4420
- continue # already present — skip
4421
-
4422
- existing.append(new_hook)
4423
- settings.setdefault("hooks", {})[hook_event] = existing
4424
- try:
4425
- with open(settings_path, "w", encoding="utf-8") as f:
4426
- json.dump(settings, f, indent=2)
4427
- updated += 1
4428
- except Exception:
4429
- pass
4555
+ # No shared table: each project's entries are built against the cli_dir
4556
+ # that project's existing C3 hooks already point at.
4557
+ updated += migrate_hooks_for_project(path)
4430
4558
 
4431
4559
  if updated:
4432
4560
  logging.getLogger(__name__).info(
4433
- "[c3] Migrated hook_c3read to %d project settings file(s)", updated
4561
+ "[c3] Migrated C3 hooks into %d project settings file(s)", updated
4434
4562
  )
4435
4563
 
4436
4564
 
@@ -4478,21 +4606,39 @@ def run_hub(
4478
4606
  pass
4479
4607
  _migrate_project_hooks()
4480
4608
 
4481
- # Auto-launch UI servers for projects with autostart_ui=True
4482
- def _autostart_ui_servers():
4609
+ # Periodic UI sweep. Before 2.128 this ran ONCE, two seconds after
4610
+ # startup, over autostart_ui projects — so a project whose session started
4611
+ # after the hub (the normal case), or whose UI died mid-session, never got
4612
+ # one. Now it also reaps the session-owned servers whose session is gone.
4613
+ def _ui_sweep_loop():
4483
4614
  import time
4484
4615
  time.sleep(2) # Give Flask a moment to bind
4485
- try:
4486
- pm = _pm()
4487
- for proj_path in pm.get_autostart_projects():
4488
- try:
4489
- pm.launch_session(proj_path)
4490
- except Exception:
4491
- pass
4492
- except Exception:
4493
- pass
4616
+ first_pass = True
4617
+ while True:
4618
+ try:
4619
+ pm = _pm()
4620
+ pm.sweep_registry()
4621
+ wanted = set(pm.get_autostart_projects())
4622
+ if wanted:
4623
+ for proj in pm.list_projects():
4624
+ if proj.get("path") not in wanted or proj.get("ui_active"):
4625
+ continue
4626
+ # First pass keeps the old contract ("autostart launches
4627
+ # the UI when the hub starts"); later passes only chase
4628
+ # projects that actually have a live session.
4629
+ if not (first_pass or proj.get("session_active")):
4630
+ continue
4631
+ try:
4632
+ pm.launch_session(proj["path"])
4633
+ except Exception:
4634
+ pass
4635
+ except Exception:
4636
+ pass
4637
+ first_pass = False
4638
+ time.sleep(_UI_SWEEP_INTERVAL_S)
4494
4639
 
4495
- threading.Thread(target=_autostart_ui_servers, daemon=True).start()
4640
+ threading.Thread(target=_ui_sweep_loop, daemon=True,
4641
+ name="c3-hub-ui-sweep").start()
4496
4642
 
4497
4643
  bind_host = str(cfg.get("host", "127.0.0.1") or "127.0.0.1").strip()
4498
4644
  if bind_host not in ("127.0.0.1", "localhost", "::1") and not quiet:
@@ -493,7 +493,12 @@ function ProjectCard({ p, isChild, rollup, expanded, onToggleExpand, onChanged,
493
493
  } catch (err) { notify('IDE: ' + err.message, 'err'); }
494
494
  };
495
495
 
496
- const portLabel = p.port ? `:${p.port}` : (p.session_active ? 'MCP' : '—');
496
+ // session_count > 1 = two IDEs / a repo and its worktrees on one project.
497
+ const sessionCount = p.session_count || (p.session_active ? 1 : 0);
498
+ const mcpLabel = sessionCount > 1 ? `MCP×${sessionCount}` : 'MCP';
499
+ const portLabel = p.port
500
+ ? (sessionCount > 1 ? `:${p.port} · ${mcpLabel}` : `:${p.port}`)
501
+ : (p.session_active ? mcpLabel : '—');
497
502
  const metaText = `${ideLabel(p.ide)} · ${portLabel} · ${p.last_session ? timeAgo(p.last_session) : 'never'}`;
498
503
 
499
504
  const identity = (
@@ -321,6 +321,33 @@ async def lifespan(server):
321
321
  **({"host_session_id": _host_sid} if _host_sid else {}),
322
322
  })
323
323
 
324
+ # Liveness heartbeat: THIS process is the session, so say so in a file a
325
+ # reader can trust. Without it the hub has to infer liveness from tool
326
+ # activity, which calls a quiet-but-open session dead after 20 min and a
327
+ # closed one live for 20 min. Refreshed on a 60 s daemon thread (same
328
+ # shape as _bg_convo_sync) and removed in the finally block below.
329
+ _heartbeat_stop = threading.Event()
330
+ _live_session_id = str((services.session_mgr.current_session or {}).get("id") or "")
331
+ if _live_session_id:
332
+ from services import session_live
333
+
334
+ def _do_beat():
335
+ session_live.beat(services.project_path, _live_session_id,
336
+ host_session_id=_host_session_id(services) or "",
337
+ ide=_IDE_NAME)
338
+
339
+ _do_beat()
340
+
341
+ def _bg_heartbeat():
342
+ while not _heartbeat_stop.wait(timeout=session_live.HEARTBEAT_INTERVAL_S):
343
+ try:
344
+ _do_beat()
345
+ except Exception:
346
+ pass
347
+
348
+ threading.Thread(target=_bg_heartbeat, daemon=True,
349
+ name="c3-session-heartbeat").start()
350
+
324
351
  # Auto-restore latest snapshot if recent (< 30 min).
325
352
  # Deferred to background thread so first tool call isn't blocked.
326
353
  # Skipped in benchmark mode to prevent snapshot budget from carrying over between tasks.
@@ -391,6 +418,15 @@ async def lifespan(server):
391
418
  yield services
392
419
  finally:
393
420
  _convo_sync_stop.set()
421
+ # Stop claiming to be alive before anything slow runs: a reader that
422
+ # polls during teardown should already see this session as gone.
423
+ _heartbeat_stop.set()
424
+ if _live_session_id:
425
+ try:
426
+ from services import session_live as _sl
427
+ _sl.clear(services.project_path, _live_session_id)
428
+ except Exception:
429
+ pass
394
430
  # Auto-memory: extract remaining learnings and generate session summary.
395
431
  if hasattr(services, "auto_memory"):
396
432
  try: