code-context-control 2.138.0__tar.gz → 2.139.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 (556) hide show
  1. {code_context_control-2.138.0 → code_context_control-2.139.0}/PKG-INFO +1 -1
  2. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/c3.py +15 -2
  3. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/commands/parser.py +2 -1
  4. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/tools.html +10 -1
  5. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_access_guard.py +71 -3
  6. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/mcp_server.py +4 -1
  7. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/delegate.py +286 -12
  8. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/status.py +3 -1
  9. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/PKG-INFO +1 -1
  10. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/SOURCES.txt +4 -0
  11. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/config.py +4 -0
  12. {code_context_control-2.138.0 → code_context_control-2.139.0}/pyproject.toml +3 -1
  13. code_context_control-2.139.0/services/bench/delegate_write_eval.py +370 -0
  14. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/claude_md.py +1 -1
  15. code_context_control-2.139.0/services/delegate_write.py +243 -0
  16. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/telemetry.py +5 -1
  17. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_adoption_d4.py +9 -1
  18. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_telemetry_d0.py +18 -0
  19. code_context_control-2.139.0/tests/test_delegate_write.py +477 -0
  20. code_context_control-2.139.0/tests/test_delegate_write_eval.py +48 -0
  21. {code_context_control-2.138.0 → code_context_control-2.139.0}/LICENSE +0 -0
  22. {code_context_control-2.138.0 → code_context_control-2.139.0}/README.md +0 -0
  23. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/__init__.py +0 -0
  24. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/_hook_utils.py +0 -0
  25. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/_shell_writes.py +0 -0
  26. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/commands/__init__.py +0 -0
  27. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/commands/common.py +0 -0
  28. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/docs.html +0 -0
  29. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/edits.html +0 -0
  30. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/access.html +0 -0
  31. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/bitbucket.html +0 -0
  32. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/credentials.html +0 -0
  33. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/getting-started.html +0 -0
  34. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/index.html +0 -0
  35. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/masking.html +0 -0
  36. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/oracle.html +0 -0
  37. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/shared.css +0 -0
  38. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/guide/workflow.html +0 -0
  39. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_artifact.py +0 -0
  40. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_auto_snapshot.py +0 -0
  41. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_c3_signal.py +0 -0
  42. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_c3read.py +0 -0
  43. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_codex_lifecycle.py +0 -0
  44. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_dispatch.py +0 -0
  45. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_edit_ledger.py +0 -0
  46. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_edit_unlock.py +0 -0
  47. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_filter.py +0 -0
  48. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_ghost_files.py +0 -0
  49. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_pretool_enforce.py +0 -0
  50. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_prompt_recall.py +0 -0
  51. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_read.py +0 -0
  52. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_session_end.py +0 -0
  53. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_session_open.py +0 -0
  54. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_session_stats.py +0 -0
  55. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hook_terse_advisor.py +0 -0
  56. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_server.py +0 -0
  57. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/app.js +0 -0
  58. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/add_project.js +0 -0
  59. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/config_editor.js +0 -0
  60. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  61. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_health.js +0 -0
  62. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_panel.js +0 -0
  63. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  64. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  65. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/drill_views.js +0 -0
  66. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/global_search.js +0 -0
  67. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_access.js +0 -0
  68. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_ci.js +0 -0
  69. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  70. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  71. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  72. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_locks.js +0 -0
  73. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  74. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  75. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/modals.js +0 -0
  76. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/project_card.js +0 -0
  77. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/project_tree.js +0 -0
  78. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/session_drawer.js +0 -0
  79. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/settings_modal.js +0 -0
  80. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/sidebar.js +0 -0
  81. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/summary_bar.js +0 -0
  82. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/task_board.js +0 -0
  83. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/toasts.js +0 -0
  84. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/components/topbar.js +0 -0
  85. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui/state.js +0 -0
  86. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/hub_ui.html +0 -0
  87. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/mcp_proxy.py +0 -0
  88. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/progress.py +0 -0
  89. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/server.py +0 -0
  90. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/__init__.py +0 -0
  91. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/_grants.py +0 -0
  92. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/_helpers.py +0 -0
  93. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/agent.py +0 -0
  94. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/artifacts.py +0 -0
  95. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/bitbucket.py +0 -0
  96. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/ci.py +0 -0
  97. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/compress.py +0 -0
  98. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/credentials.py +0 -0
  99. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/edit.py +0 -0
  100. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/edit_verify.py +0 -0
  101. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/edits.py +0 -0
  102. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/federate.py +0 -0
  103. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/filter.py +0 -0
  104. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/impact.py +0 -0
  105. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/jira.py +0 -0
  106. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/locks.py +0 -0
  107. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/memory.py +0 -0
  108. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/override.py +0 -0
  109. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/project.py +0 -0
  110. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/read.py +0 -0
  111. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/search.py +0 -0
  112. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/session.py +0 -0
  113. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/shell.py +0 -0
  114. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/shell_job.py +0 -0
  115. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/shell_nudge.py +0 -0
  116. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/shell_parsers.py +0 -0
  117. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/shell_render.py +0 -0
  118. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/tasks.py +0 -0
  119. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/tools/validate.py +0 -0
  120. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/api.js +0 -0
  121. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/app.js +0 -0
  122. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/access.js +0 -0
  123. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/bitbucket.js +0 -0
  124. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/chat.js +0 -0
  125. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/credentials.js +0 -0
  126. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/dashboard.js +0 -0
  127. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/edits.js +0 -0
  128. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/enforcement.js +0 -0
  129. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/instructions.js +0 -0
  130. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/jira.js +0 -0
  131. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/memory.js +0 -0
  132. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/sessions.js +0 -0
  133. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/settings.js +0 -0
  134. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/sidebar.js +0 -0
  135. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/components/tasks.js +0 -0
  136. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/icons.js +0 -0
  137. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/pm_shared.js +0 -0
  138. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/shared.js +0 -0
  139. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui/theme.js +0 -0
  140. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui.html +0 -0
  141. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui_legacy.html +0 -0
  142. {code_context_control-2.138.0 → code_context_control-2.139.0}/cli/ui_nano.html +0 -0
  143. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  144. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/entry_points.txt +0 -0
  145. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/requires.txt +0 -0
  146. {code_context_control-2.138.0 → code_context_control-2.139.0}/code_context_control.egg-info/top_level.txt +0 -0
  147. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/__init__.py +0 -0
  148. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/codex_patch.py +0 -0
  149. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/grok_payload.py +0 -0
  150. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/hook_command.py +0 -0
  151. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/host.py +0 -0
  152. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/ide.py +0 -0
  153. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/mcp_toml.py +0 -0
  154. {code_context_control-2.138.0 → code_context_control-2.139.0}/core/web_security.py +0 -0
  155. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/__init__.py +0 -0
  156. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/config.py +0 -0
  157. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/listeners.py +0 -0
  158. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/mcp_oracle.py +0 -0
  159. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/oracle_server.py +0 -0
  160. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/oracle_ui.html +0 -0
  161. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/__init__.py +0 -0
  162. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/activity_reporter.py +0 -0
  163. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/api_auth.py +0 -0
  164. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/c3_bridge.py +0 -0
  165. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/chat_engine.py +0 -0
  166. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/chat_poll.py +0 -0
  167. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/chat_store.py +0 -0
  168. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/client_tokens.py +0 -0
  169. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/cross_memory.py +0 -0
  170. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/discovery_audit.py +0 -0
  171. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/federated_graph.py +0 -0
  172. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/health_checker.py +0 -0
  173. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/insight_engine.py +0 -0
  174. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/local_session.py +0 -0
  175. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/memory_reader.py +0 -0
  176. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/memory_writer.py +0 -0
  177. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/mobile_api.py +0 -0
  178. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/project_scanner.py +0 -0
  179. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/review_agent.py +0 -0
  180. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/tool_executor.py +0 -0
  181. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/services/tool_registry.py +0 -0
  182. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/activity.js +0 -0
  183. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/agents.js +0 -0
  184. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/app.js +0 -0
  185. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/busy.js +0 -0
  186. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/conversations.js +0 -0
  187. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/input.js +0 -0
  188. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/markdown.js +0 -0
  189. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/send.js +0 -0
  190. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/stream_renderer.js +0 -0
  191. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/chat/toolbar.js +0 -0
  192. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/core.js +0 -0
  193. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/crossgraph.js +0 -0
  194. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/header.js +0 -0
  195. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/insights.js +0 -0
  196. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/projects.js +0 -0
  197. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/qrcode.js +0 -0
  198. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/settings.js +0 -0
  199. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/suggestions.js +0 -0
  200. {code_context_control-2.138.0 → code_context_control-2.139.0}/oracle/ui/theme_tabs.js +0 -0
  201. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/__init__.py +0 -0
  202. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/access_guard.py +0 -0
  203. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/access_telemetry.py +0 -0
  204. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/activity_log.py +0 -0
  205. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/agent_base.py +0 -0
  206. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/agent_locks.py +0 -0
  207. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/agents.py +0 -0
  208. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/agents_workflow.py +0 -0
  209. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/artifact_defs.py +0 -0
  210. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/artifact_store.py +0 -0
  211. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/atomic_json.py +0 -0
  212. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/auto_memory.py +0 -0
  213. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/background_service.py +0 -0
  214. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/__init__.py +0 -0
  215. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/delegate_eval.py +0 -0
  216. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/external/__init__.py +0 -0
  217. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/external/aider_polyglot.py +0 -0
  218. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/external/swe_bench.py +0 -0
  219. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/map_eval.py +0 -0
  220. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/search_eval.py +0 -0
  221. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bench/shell_eval.py +0 -0
  222. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/benchmark_dashboard.py +0 -0
  223. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bitbucket_client.py +0 -0
  224. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/bitbucket_credentials.py +0 -0
  225. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_act.py +0 -0
  226. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_cache.py +0 -0
  227. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_expr.py +0 -0
  228. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_failures.py +0 -0
  229. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_github.py +0 -0
  230. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_impact.py +0 -0
  231. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_intel.py +0 -0
  232. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_runner.py +0 -0
  233. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ci_workflow.py +0 -0
  234. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/circuit_breaker.py +0 -0
  235. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/codex_integration.py +0 -0
  236. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/codex_transcript.py +0 -0
  237. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/compressor.py +0 -0
  238. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/context_snapshot.py +0 -0
  239. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/conversation_store.py +0 -0
  240. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/cred_audit.py +0 -0
  241. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/cred_telemetry.py +0 -0
  242. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/credential_store.py +0 -0
  243. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/delegate_agents.py +0 -0
  244. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/dir_map.py +0 -0
  245. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/doc_index.py +0 -0
  246. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/e2e_benchmark.py +0 -0
  247. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/e2e_evaluator.py +0 -0
  248. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/e2e_tasks.py +0 -0
  249. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/edit_ledger.py +0 -0
  250. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/embedding_index.py +0 -0
  251. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/enforcement_policy.py +0 -0
  252. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/error_reporting.py +0 -0
  253. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/file_map.py +0 -0
  254. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/file_memory.py +0 -0
  255. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/git_context.py +0 -0
  256. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/grok_integration.py +0 -0
  257. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/grok_transcript.py +0 -0
  258. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/host_sessions.py +0 -0
  259. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/hub_service.py +0 -0
  260. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/index_store.py +0 -0
  261. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/indexer.py +0 -0
  262. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/jira_client.py +0 -0
  263. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/jira_cloud.py +0 -0
  264. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/jira_credentials.py +0 -0
  265. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/jira_data_center.py +0 -0
  266. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/jira_links.py +0 -0
  267. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/lexical_index.py +0 -0
  268. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/mask_activation.py +0 -0
  269. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/mask_mirror.py +0 -0
  270. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/mask_presets.py +0 -0
  271. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory.py +0 -0
  272. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_consolidator.py +0 -0
  273. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_distiller.py +0 -0
  274. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_graph.py +0 -0
  275. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_grounder.py +0 -0
  276. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_queue.py +0 -0
  277. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/memory_scorer.py +0 -0
  278. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/metrics.py +0 -0
  279. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/notifications.py +0 -0
  280. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ollama_bridge.py +0 -0
  281. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ollama_client.py +0 -0
  282. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/ollama_credentials.py +0 -0
  283. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/oracle_service.py +0 -0
  284. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/output_filter.py +0 -0
  285. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/override_costs.py +0 -0
  286. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/override_grants.py +0 -0
  287. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/override_policy.py +0 -0
  288. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/override_requests.py +0 -0
  289. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/override_wake.py +0 -0
  290. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/parser.py +0 -0
  291. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/project_manager.py +0 -0
  292. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/project_runtime.py +0 -0
  293. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/protocol.py +0 -0
  294. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/proxy_state.py +0 -0
  295. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/repo_map.py +0 -0
  296. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/repo_shape.py +0 -0
  297. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/reranker.py +0 -0
  298. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/retention.py +0 -0
  299. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/retrieval.py +0 -0
  300. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/retrieval_broker.py +0 -0
  301. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/router.py +0 -0
  302. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/runtime.py +0 -0
  303. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/scanner.py +0 -0
  304. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/session_benchmark.py +0 -0
  305. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/session_live.py +0 -0
  306. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/session_manager.py +0 -0
  307. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/session_preloader.py +0 -0
  308. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/shell_jobs.py +0 -0
  309. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/shell_output.py +0 -0
  310. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/subprojects.py +0 -0
  311. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/task_store.py +0 -0
  312. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/text_index.py +0 -0
  313. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/time_tracker.py +0 -0
  314. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/tool_classifier.py +0 -0
  315. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/transcript_index.py +0 -0
  316. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/validation_cache.py +0 -0
  317. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/vector_store.py +0 -0
  318. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/watcher.py +0 -0
  319. {code_context_control-2.138.0 → code_context_control-2.139.0}/services/win_subprocess.py +0 -0
  320. {code_context_control-2.138.0 → code_context_control-2.139.0}/setup.cfg +0 -0
  321. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_builtin_optout.py +0 -0
  322. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_guard.py +0 -0
  323. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_guard_meta.py +0 -0
  324. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_guard_shell_project.py +0 -0
  325. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_guard_surfaces.py +0 -0
  326. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_access_guard_wiring.py +0 -0
  327. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_activity_log_find_last.py +0 -0
  328. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_activity_reporter.py +0 -0
  329. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_agent_locks.py +0 -0
  330. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_aider_polyglot.py +0 -0
  331. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_antigravity_transition.py +0 -0
  332. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_artifact_store.py +0 -0
  333. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_artifact_tool.py +0 -0
  334. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_atomic_json.py +0 -0
  335. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_background_service.py +0 -0
  336. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  337. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_bitbucket_client.py +0 -0
  338. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_bitbucket_config_fallback.py +0 -0
  339. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_bitbucket_credentials.py +0 -0
  340. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_bitbucket_tool.py +0 -0
  341. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_builtin_modes.py +0 -0
  342. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  343. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_c3_shell.py +0 -0
  344. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_chat_poll.py +0 -0
  345. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_act.py +0 -0
  346. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_cache.py +0 -0
  347. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_dag_ownership.py +0 -0
  348. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_expr.py +0 -0
  349. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_impact.py +0 -0
  350. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_intel.py +0 -0
  351. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_runner.py +0 -0
  352. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ci_workflow.py +0 -0
  353. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_circuit_breaker.py +0 -0
  354. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_claude_md_merge.py +0 -0
  355. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_claude_md_updater.py +0 -0
  356. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_cli_smoke.py +0 -0
  357. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_codex_mcp_transport.py +0 -0
  358. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_codex_native.py +0 -0
  359. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_compressor_large_file.py +0 -0
  360. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_confirm_guard.py +0 -0
  361. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_confirm_guard_wiring.py +0 -0
  362. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_cooccurrence_bounds.py +0 -0
  363. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_cred_audit.py +0 -0
  364. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_cred_telemetry.py +0 -0
  365. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_credential_login_kind.py +0 -0
  366. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_credential_store.py +0 -0
  367. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_credential_ui_parity.py +0 -0
  368. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_credentials_routes.py +0 -0
  369. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_credentials_tool.py +0 -0
  370. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_creds_cli_smoke.py +0 -0
  371. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_cascade.py +0 -0
  372. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_claude_d1.py +0 -0
  373. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_eval.py +0 -0
  374. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_grok.py +0 -0
  375. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_host_d2.py +0 -0
  376. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_scout_d3.py +0 -0
  377. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_thinking_cap.py +0 -0
  378. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_tuning.py +0 -0
  379. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_delegate_version_probe.py +0 -0
  380. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_dir_map_c3.py +0 -0
  381. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_e2e_benchmark.py +0 -0
  382. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_edit_ledger_deadline.py +0 -0
  383. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_edit_ledger_hook.py +0 -0
  384. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_edit_locking.py +0 -0
  385. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_edit_normalization.py +0 -0
  386. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_edit_verify.py +0 -0
  387. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_embedding_index_deadlock.py +0 -0
  388. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_enforcement_flip.py +0 -0
  389. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_enforcement_policy.py +0 -0
  390. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_enforcement_routes.py +0 -0
  391. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_env_parser.py +0 -0
  392. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  393. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_federated_graph.py +0 -0
  394. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_file_map_c1.py +0 -0
  395. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_filter_backoff.py +0 -0
  396. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ghost_files.py +0 -0
  397. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ghost_generation.py +0 -0
  398. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_git_branch_awareness.py +0 -0
  399. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_grok_native.py +0 -0
  400. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_access_guard.py +0 -0
  401. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_codex_compat.py +0 -0
  402. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_dispatch.py +0 -0
  403. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_grok_compat.py +0 -0
  404. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_pretool_enforce.py +0 -0
  405. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_prompt_recall.py +0 -0
  406. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_session_end.py +0 -0
  407. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_smoke.py +0 -0
  408. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_state.py +0 -0
  409. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hook_utf8_stdio.py +0 -0
  410. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_credentials_route.py +0 -0
  411. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_credentials_routes_write.py +0 -0
  412. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_enforcement_routes.py +0 -0
  413. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_hook_migration.py +0 -0
  414. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_inspect_api.py +0 -0
  415. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_locks_routes.py +0 -0
  416. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_oracle_service_routes.py +0 -0
  417. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_override_policy_routes.py +0 -0
  418. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_override_routes.py +0 -0
  419. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_server_smoke.py +0 -0
  420. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_hub_subproject_links.py +0 -0
  421. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ide_workflow_adaptation.py +0 -0
  422. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  423. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  424. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_init_scan.py +0 -0
  425. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_install_mcp_entrypoint.py +0 -0
  426. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_cli_smoke.py +0 -0
  427. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_client.py +0 -0
  428. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_config_fallback.py +0 -0
  429. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_credentials.py +0 -0
  430. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_links.py +0 -0
  431. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_routes.py +0 -0
  432. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_jira_tool.py +0 -0
  433. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_large_files_c4.py +0 -0
  434. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_lazy_store_init.py +0 -0
  435. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_map_eval.py +0 -0
  436. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_map_telemetry_c0.py +0 -0
  437. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mask_guard.py +0 -0
  438. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mask_routes.py +0 -0
  439. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mask_surfaces.py +0 -0
  440. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mcp_host_guard.py +0 -0
  441. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mcp_memory_timeout.py +0 -0
  442. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mcp_server_smoke.py +0 -0
  443. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mcp_toml.py +0 -0
  444. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_memory_distiller.py +0 -0
  445. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_memory_graph_api.py +0 -0
  446. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_memory_queue.py +0 -0
  447. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_memory_system.py +0 -0
  448. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_api.py +0 -0
  449. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_clients.py +0 -0
  450. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_extras.py +0 -0
  451. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_jobs_ci.py +0 -0
  452. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_override_costs.py +0 -0
  453. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_override_routes.py +0 -0
  454. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_mobile_security_api.py +0 -0
  455. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_no_stale_model_pins.py +0 -0
  456. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_notification_dedup.py +0 -0
  457. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_notification_discipline.py +0 -0
  458. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_notification_kinds.py +0 -0
  459. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ollama_credentials.py +0 -0
  460. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_api_auth.py +0 -0
  461. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_apikey_api.py +0 -0
  462. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_c3_bridge.py +0 -0
  463. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_chat_engine.py +0 -0
  464. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_chat_store.py +0 -0
  465. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_discovery_api.py +0 -0
  466. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_discovery_audit.py +0 -0
  467. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_health_probe.py +0 -0
  468. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_local_auth.py +0 -0
  469. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_loopback.py +0 -0
  470. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_ollama_bridge.py +0 -0
  471. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_scanner_cache.py +0 -0
  472. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_security_fixes.py +0 -0
  473. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_session_bootstrap.py +0 -0
  474. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_subproject_awareness.py +0 -0
  475. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_oracle_ui_bundle.py +0 -0
  476. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_output_filter.py +0 -0
  477. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_override_costs.py +0 -0
  478. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_override_grants.py +0 -0
  479. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_override_grants_mcp.py +0 -0
  480. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_override_requests.py +0 -0
  481. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_override_wake.py +0 -0
  482. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_permissions.py +0 -0
  483. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_pm_api.py +0 -0
  484. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_project_manager.py +0 -0
  485. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_project_manager_merge.py +0 -0
  486. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_project_tool.py +0 -0
  487. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_read_coercion.py +0 -0
  488. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_read_edit_parity.py +0 -0
  489. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_read_outside_root.py +0 -0
  490. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_related_facts.py +0 -0
  491. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_repo_map.py +0 -0
  492. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_repo_shape.py +0 -0
  493. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_response_boilerplate.py +0 -0
  494. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_retention.py +0 -0
  495. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_review_digest.py +0 -0
  496. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_eval.py +0 -0
  497. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p1.py +0 -0
  498. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p2.py +0 -0
  499. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p2b.py +0 -0
  500. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p3.py +0 -0
  501. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p4.py +0 -0
  502. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_search_p5.py +0 -0
  503. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_service_durability.py +0 -0
  504. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_session_benchmark.py +0 -0
  505. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_session_budget.py +0 -0
  506. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_session_live.py +0 -0
  507. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_budget_s1.py +0 -0
  508. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_creds.py +0 -0
  509. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_eval.py +0 -0
  510. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_jobs_s3.py +0 -0
  511. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_nudge_s4.py +0 -0
  512. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_output.py +0 -0
  513. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_parsers_s2.py +0 -0
  514. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_robustness.py +0 -0
  515. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_telemetry_s0.py +0 -0
  516. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_write_ledger.py +0 -0
  517. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_shell_writes.py +0 -0
  518. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subproject_clear_scope.py +0 -0
  519. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subproject_depth_consumers.py +0 -0
  520. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subproject_exclusion.py +0 -0
  521. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subproject_federation.py +0 -0
  522. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subproject_linkage_api.py +0 -0
  523. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_subprojects.py +0 -0
  524. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_swe_bench.py +0 -0
  525. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_task_store.py +0 -0
  526. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_task_tool.py +0 -0
  527. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_time_tracker.py +0 -0
  528. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_token_telemetry.py +0 -0
  529. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_token_tracking.py +0 -0
  530. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_tool_registry.py +0 -0
  531. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_ui_jsx_syntax.py +0 -0
  532. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_upgrade_and_version.py +0 -0
  533. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_validate.py +0 -0
  534. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_vault_write_guard.py +0 -0
  535. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_version_sync.py +0 -0
  536. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_watcher_pruning.py +0 -0
  537. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_web_security.py +0 -0
  538. {code_context_control-2.138.0 → code_context_control-2.139.0}/tests/test_windows_reliability.py +0 -0
  539. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/__init__.py +0 -0
  540. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/backend.py +0 -0
  541. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/main.py +0 -0
  542. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/__init__.py +0 -0
  543. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/benchmark_view.py +0 -0
  544. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/claudemd_view.py +0 -0
  545. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/compress_view.py +0 -0
  546. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/index_view.py +0 -0
  547. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/init_view.py +0 -0
  548. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/mcp_view.py +0 -0
  549. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/optimize_view.py +0 -0
  550. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/pipe_view.py +0 -0
  551. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/projects_view.py +0 -0
  552. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/search_view.py +0 -0
  553. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/session_view.py +0 -0
  554. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/stats.py +0 -0
  555. {code_context_control-2.138.0 → code_context_control-2.139.0}/tui/screens/ui_view.py +0 -0
  556. {code_context_control-2.138.0 → code_context_control-2.139.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.138.0
3
+ Version: 2.139.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
@@ -91,7 +91,7 @@ console = Console() if HAS_RICH else None
91
91
  # Config
92
92
  CONFIG_DIR = ".c3"
93
93
  CONFIG_FILE = ".c3/config.json"
94
- __version__ = "2.138.0"
94
+ __version__ = "2.139.0"
95
95
 
96
96
 
97
97
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -1583,6 +1583,19 @@ def cmd_delegate_eval(args):
1583
1583
 
1584
1584
  targets = [t.strip() for t in (args.targets or "").split(",") if t.strip()]
1585
1585
  case_ids = [c.strip() for c in (args.cases or "").split(",") if c.strip()]
1586
+ if args.suite == "write":
1587
+ # Write mode (docs/delegate-write.md): graded on the files left behind.
1588
+ from services.bench import delegate_write_eval as dwe
1589
+ if not targets:
1590
+ raise RuntimeError("--suite write needs --targets (e.g. claude:small,claude:medium, or gold)")
1591
+ try:
1592
+ overrides = json.loads(args.config) if getattr(args, "config", None) else None
1593
+ report = dwe.run_write_suite(targets, case_ids=case_ids or None, record=args.record,
1594
+ delegate_overrides=overrides)
1595
+ except ValueError as e:
1596
+ raise RuntimeError(str(e))
1597
+ print(json.dumps(report, indent=2) if args.json else dwe.render(report))
1598
+ return
1586
1599
  overrides = json.loads(args.config) if getattr(args, "config", None) else None
1587
1600
  if overrides is not None and not isinstance(overrides, dict):
1588
1601
  raise RuntimeError("--config must be a JSON object")
@@ -5065,7 +5078,7 @@ back to native tools as the task progresses.
5065
5078
  - **Filter**: `c3_filter(text=...)` — for terminal output >10 lines
5066
5079
  - **Shell**: `c3_shell(cmd, timeout=60)` — structured shell exec (tests/git/build). Auto-filters output, logs git mutations to the ledger. Native Bash for interactive/TTY only
5067
5080
  - **Memory**: `c3_memory(action='recall')` — full recall. `index` + `fetch` for token-efficient two-step retrieval
5068
- - **Delegate**: `c3_delegate(task, task_type, context|file_path)` — your own provider one tier down (default `backend='host'`, `tier='small'`; Claude Code: Haiku) for bounded answers: summarize a diff/log, explain a function, triage a traceback. `scout=true` lets it look files up itself. Multi-step work: the `c3-scout` (haiku) / `c3-worker` (sonnet) subagents
5081
+ - **Delegate**: `c3_delegate(task, task_type, context|file_path)` — your own provider one tier down (default `backend='host'`, `tier='small'`; Claude Code: Haiku) for bounded answers: summarize a diff/log, explain a function, triage a traceback. `scout=true` lets it look files up itself; `write_paths='a.py,b.py'` has Sonnet make a change you specified in only those files and return the diff. Multi-step work: the `c3-scout` (haiku) / `c3-worker` (sonnet) subagents
5069
5082
  - **Local CI** (v2.79.0+): `c3_ci(action='inspect|run|rerun|failures')` — run THIS repo's real `.github/workflows` here instead of pushing for feedback. `run` executes in `needs` order; `failures` gives {file,line,message}; `rerun` retries only what failed. Only `FULL_CI_PASS` means safe to push — `PARTIAL_PASS` means something did not run (other OS, unsupported action, or your selection).
5070
5083
  - **Bitbucket** (v2.30.0+, when `c3 bitbucket login` has run): `c3_bitbucket(action='list_prs|get_pr|merge_pr|...')` — self-hosted Bitbucket Data Center / Server. Token in OS keyring; mutating actions auto-log to the edit ledger.
5071
5084
  - **Cross-project** (v2.31.0+): `c3_project(action='list|scan|search|read|edit|...', project='<name|path>')` — discover and operate on OTHER c3-installed projects. Reads run freely; writes (edit/shell/memory) need `allow_write=true`.
@@ -92,7 +92,8 @@ def build_parser(version: str, parse_cli_ide_arg):
92
92
  p_delegate_eval = subparsers.add_parser(
93
93
  "delegate-eval", help="Grade c3_delegate answers per backend/tier (docs/delegate-eval.md)")
94
94
  p_delegate_eval.add_argument("--suite", default="gold",
95
- help="'gold' (bundled) or a .jsonl path")
95
+ help="'gold' (bundled), 'write' (write mode, docs/delegate-write.md) "
96
+ "or a .jsonl path")
96
97
  p_delegate_eval.add_argument("--targets", default="",
97
98
  help="Comma-separated backend[:tier] list, e.g. 'claude:small,claude:medium'")
98
99
  p_delegate_eval.add_argument("--cases", default="", help="Comma-separated case ids (default: all)")
@@ -954,6 +954,12 @@ c3_status(view=<span class="str">'sessions'</span>) <span class="com">#
954
954
  <td class="param-default">false</td>
955
955
  <td class="param-desc">Let the delegate Read/Grep/Glob the project itself, for lookups when you cannot name the files. Claude runs <code>--restricted</code> with Access Guard's read denies; Codex uses its read-only sandbox.</td>
956
956
  </tr>
957
+ <tr>
958
+ <td class="param-name">write_paths</td>
959
+ <td class="param-type">string</td>
960
+ <td class="param-default">""</td>
961
+ <td class="param-desc">Write mode (Claude only, defaults to Sonnet). Comma-separated project-relative globs the delegate may edit or create. You decide the change; it makes it with Read/Grep/Glob/Edit/Write, no commands. You get back the diff, and each change is in the edit ledger. Access Guard applies; confirm-held, locked, <code>.git/</code> and <code>.c3/</code> paths are refused. Review the diff and run the tests yourself.</td>
962
+ </tr>
957
963
  </tbody>
958
964
  </table>
959
965
 
@@ -965,7 +971,10 @@ c3_delegate(task=<span class="str">'Summarize this diff for a changelog in three
965
971
  c3_delegate(task=<span class="str">'What does resolve_host return inside a grok child?'</span>, file_path=<span class="str">'core/host.py'</span>, task_type=<span class="str">'explain'</span>, tier=<span class="str">'medium'</span>)
966
972
 
967
973
  <span class="com"># A lookup the delegate does itself</span>
968
- c3_delegate(task=<span class="str">'Which functions call charge_card, and where?'</span>, scout=<span class="kw">True</span>)</code></pre>
974
+ c3_delegate(task=<span class="str">'Which functions call charge_card, and where?'</span>, scout=<span class="kw">True</span>)
975
+
976
+ <span class="com"># A change you have decided, made by Sonnet inside a write set</span>
977
+ c3_delegate(task=<span class="str">'Rename calc_total to order_total in its definition and every use; no alias'</span>, write_paths=<span class="str">'inv/orders.py, inv/report.py, tests/test_orders.py'</span>)</code></pre>
969
978
 
970
979
  <div class="callout callout-info">
971
980
  <span class="callout-icon">ℹ️</span>
@@ -503,14 +503,77 @@ def run(payload: dict, project_path: Path | None = None,
503
503
  return None
504
504
 
505
505
 
506
+ def _worker_refusal(rel: str, why: str) -> dict:
507
+ return _deny(f"[c3-delegate:refused] {rel}: {why}. Do not write it another way; "
508
+ "leave it and say in your report what change it needed.")
509
+
510
+
511
+ def worker_run(payload: dict, project_path: Path, state_dir: Path) -> dict | None:
512
+ """PreToolUse for a ``c3_delegate`` write-mode worker (see services/delegate_write).
513
+
514
+ Reads go through ``run`` exactly as for a scout. A write must pass, in
515
+ order: canonicalization inside the project, never ``.git/`` or ``.c3/``,
516
+ never the credential vault, Access Guard with NO grants and NO filed
517
+ requests (a confirm hold is a refusal here — a headless worker cannot
518
+ wait for a human, and the caller can make that edit itself), the caller's
519
+ write set, and another agent's lock. Then the pre-image is saved before
520
+ the write is allowed. Raises on a broken spec; main() turns that into a
521
+ deny.
522
+ """
523
+ from services import delegate_write as dw
524
+
525
+ tool = normalize_tool_name(payload.get("tool_name", ""))
526
+ if tool not in _WRITE_TOOLS:
527
+ return run(payload, project_path)
528
+ spec = dw.load_spec(state_dir)
529
+ fp = _target(payload.get("tool_input", {}) or {})
530
+ if not fp:
531
+ return _deny("[c3-delegate:refused] a write with no target path")
532
+ base = str(project_path)
533
+ canon, rel, denial = ag.canonicalize(fp, base)
534
+ if denial:
535
+ return _worker_refusal(fp, f"path refused ({denial.reason})")
536
+ if not rel:
537
+ return _worker_refusal(fp, "outside the project")
538
+ if dw.forbidden(rel):
539
+ return _worker_refusal(rel, "under .git/ or .c3/, never delegate-writable")
540
+ try:
541
+ from services import credential_store as _cs
542
+ vault = _cs.vault_guard_reason(fp)
543
+ except Exception as exc: # fail closed: an unloadable vault check refuses
544
+ vault = f"vault check unavailable ({type(exc).__name__})"
545
+ if vault:
546
+ return _worker_refusal(rel, "credential vault file")
547
+ op = "write" if os.path.exists(fp) else "create"
548
+ denial = ag.check(fp, op, base)
549
+ if denial:
550
+ _record(denial, tool, op, fp, base, str(payload.get("session_id") or ""))
551
+ return _worker_refusal(rel, f"Access Guard {denial.kind} rule '{denial.rule}' "
552
+ f"({denial.scope})")
553
+ if not dw.in_write_set(rel, spec["write_paths"]):
554
+ return _worker_refusal(rel, "outside the write set the caller gave ("
555
+ + ", ".join(spec["write_paths"]) + ")")
556
+ try:
557
+ from services import agent_locks
558
+ holder = agent_locks.check(fp, base, str(spec.get("session_id") or ""))
559
+ except Exception:
560
+ holder = None
561
+ if holder:
562
+ return _worker_refusal(rel, f"locked by {holder.get('agent_id', 'another agent')}")
563
+ dw.snapshot(state_dir, fp, canon)
564
+ return None
565
+
566
+
506
567
  def main() -> None:
507
- """Standalone PreToolUse entry for a ``c3_delegate`` scout run.
568
+ """Standalone PreToolUse entry for a ``c3_delegate`` scout or worker run.
508
569
 
509
570
  A scout is ``claude -p --restricted`` with Read/Grep/Glob and no C3 MCP
510
571
  server, so the dispatcher's discipline hook (hook_pretool_enforce) would
511
572
  refuse every native read. Only the access guard runs here. It prints a
512
573
  deny and nothing else: grants and search footers are for an agent that
513
574
  can act on them. Any failure denies (fail closed).
575
+
576
+ ``--worker-state DIR``: a write-mode worker; see ``worker_run``.
514
577
  """
515
578
  import argparse
516
579
  import json
@@ -520,6 +583,7 @@ def main() -> None:
520
583
  ensure_utf8_stdio()
521
584
  parser = argparse.ArgumentParser()
522
585
  parser.add_argument("--project", required=True)
586
+ parser.add_argument("--worker-state", default="")
523
587
  args = parser.parse_args()
524
588
  try:
525
589
  raw = sys.stdin.read()
@@ -527,9 +591,13 @@ def main() -> None:
527
591
  if not isinstance(payload, dict):
528
592
  raise ValueError("hook payload must be a JSON object")
529
593
  # access_guard canonicalizes the base itself (no direct resolve here).
530
- out = run(payload, Path(args.project))
594
+ if args.worker_state:
595
+ out = worker_run(payload, Path(args.project), Path(args.worker_state))
596
+ else:
597
+ out = run(payload, Path(args.project))
531
598
  except Exception as exc:
532
- out = _deny(f"[c3-access:error] scout guard failed ({type(exc).__name__}: {exc}); "
599
+ who = "worker" if args.worker_state else "scout"
600
+ out = _deny(f"[c3-access:error] {who} guard failed ({type(exc).__name__}: {exc}); "
533
601
  "refusing the call")
534
602
  if isinstance(out, dict) and isinstance(out.get("hookSpecificOutput"), dict):
535
603
  print(json.dumps({"hookSpecificOutput": out["hookSpecificOutput"]}))
@@ -806,11 +806,13 @@ async def c3_delegate(task: str, task_type: str = "ask", context: str = "",
806
806
  file_path: str = "", backend: str = "host",
807
807
  allow_write_delegation: bool = False,
808
808
  tier: str = "", model: str = "", scout: bool = False,
809
+ write_paths: str = "",
809
810
  ctx: Context = None) -> str:
810
811
  """OFFLOAD to another model — use when the subtask is local-model-sized or needs a different perspective.
811
812
  backend: host (default: your own provider, one tier down — Claude Code->claude, Codex->codex, Grok->grok, Antigravity->gemini) | claude|codex|gemini|grok|ollama|auto. task_type: auto, summarize, explain, review, ask, test, diagnose, ping (live auth check), available, codex_check, gemini_check, grok_check, codex_resume.
812
813
  tier: small (default; scout=true defaults to medium) | medium | large | default. claude: haiku/sonnet/opus; codex/grok: reasoning effort; gemini: flash-lite/flash/pro. model overrides the tier. Context + file_path are packed by C3 under Access Guard; a file over 8k tokens goes as its map, so for line-level questions on a big file paste the excerpt or use scout=true.
813
814
  scout=true: the delegate may Read/Grep/Glob the project itself (claude: read-only, guard-denied paths refused; codex: its read-only sandbox) — for lookups when you cannot name the files.
815
+ write_paths: comma-separated project-relative globs the delegate may edit or create (write mode; claude only, tier defaults to medium). You decide the change: say what to change, where, and the intended behaviour. It cannot run commands. You get back the diff (each change is in the edit ledger): review it and run the tests yourself. Access Guard applies; confirm-held, locked, .git/ and .c3/ paths are refused.
814
816
  grok runs read-only in a temp dir unless delegate.grok_allow_write=true (then --yolo in the project, loading its trusted .grok MCP servers and hooks).
815
817
  allow_write_delegation: explicit user opt-in for write-capable backends (gemini/grok write mode/codex_resume) while Access Guard rules are active; codex is pinned read-only instead."""
816
818
  svc = _svc(ctx)
@@ -828,7 +830,8 @@ async def c3_delegate(task: str, task_type: str = "ask", context: str = "",
828
830
  try:
829
831
  return await asyncio.to_thread(handle_delegate, task, task_type, context,
830
832
  file_path, svc, finalize, backend,
831
- allow_write_delegation, tier, model, scout)
833
+ allow_write_delegation, tier, model, scout,
834
+ write_paths)
832
835
  finally:
833
836
  svc._agent_progress_cb = None
834
837
 
@@ -364,6 +364,21 @@ _CLAUDE_SCOUT_RULES = (
364
364
 
365
365
  SCOUT_TOOLS = "Read,Grep,Glob"
366
366
 
367
+ _CLAUDE_WORKER_RULES = (
368
+ "You are a worker making one code change that a lead agent has already "
369
+ "decided. You have Read, Grep, Glob, Edit and Write in this repository and "
370
+ "nothing else: no commands, no tests, no network. Do exactly the change the "
371
+ "task describes: no refactors, renames, reformatting or extras it did not "
372
+ "ask for. Edit or create only paths in the write set; a refused path is off "
373
+ "limits, do not reach it another way. Read a file before editing it and "
374
+ "match its style. If the task is ambiguous, contradicts the code, or needs a "
375
+ "file outside the write set, do the part you can do safely and report the "
376
+ "rest instead of guessing. Make the edits first, then finish with a short "
377
+ "report: each file changed and what changed, anything left undone and why, "
378
+ "and any assumption the lead should check. The lead reviews your diff and "
379
+ "runs the tests."
380
+ )
381
+
367
382
 
368
383
  def _permission_pattern(glob: str, root: str) -> str:
369
384
  """An Access Guard glob as a Claude Code ``Read()`` permission pattern.
@@ -431,6 +446,53 @@ def scout_settings(project_path) -> dict:
431
446
  }
432
447
 
433
448
 
449
+ def worker_edit_denies(project_path) -> list[str]:
450
+ """``Edit()`` deny patterns: every deny, read_only, confirm and mask glob.
451
+
452
+ A worker never gets a grant or files a request, so a confirm hold is a
453
+ refusal for it. Deny beats allow in Claude Code, so a broad write set
454
+ cannot reopen any of these. Raises ValueError on a corrupt rule scope.
455
+ """
456
+ rules, mask_rules, corrupt = access_guard.load_all(str(project_path))
457
+ if corrupt:
458
+ raise ValueError(f"Access Guard config is unreadable in scope(s) {', '.join(corrupt)}")
459
+ globs = [r.glob for r in rules if r.kind in ("deny", "read_only", "confirm")]
460
+ globs += [m.glob for m in mask_rules]
461
+ root = Path(project_path).resolve().as_posix().casefold()
462
+ patterns = []
463
+ for glob in dict.fromkeys(globs):
464
+ pattern = _permission_pattern(glob, root)
465
+ if pattern:
466
+ patterns.append(f"Edit({pattern})")
467
+ return patterns
468
+
469
+
470
+ def worker_settings(project_path, write_globs, state_dir) -> dict:
471
+ """``--settings`` for a write-mode worker (services/delegate_write).
472
+
473
+ Edit() allow rules are the write set, rooted at the project (a bare name
474
+ is that file at the root, never a basename match anywhere). Read and
475
+ Edit denies come from Access Guard. The guard hook runs in worker mode on
476
+ every file tool: it re-checks all of that on the canonical path and saves
477
+ pre-images into ``state_dir``.
478
+ """
479
+ from cli._hook_utils import hook_command_arg
480
+
481
+ hook = Path(__file__).resolve().parents[1] / "hook_access_guard.py"
482
+ project = str(Path(project_path).resolve())
483
+ command = (f"{hook_command_arg(sys.executable)} {hook_command_arg(str(hook))} "
484
+ f"--project {hook_command_arg(project)} "
485
+ f"--worker-state {hook_command_arg(str(state_dir))}")
486
+ return {
487
+ "permissions": {
488
+ "allow": [f"Edit(./{g})" for g in write_globs],
489
+ "deny": scout_read_denies(project) + worker_edit_denies(project),
490
+ },
491
+ "hooks": {"PreToolUse": [{"matcher": "Read|Grep|Glob|Edit|Write|MultiEdit|NotebookEdit",
492
+ "hooks": [{"type": "command", "command": command}]}]},
493
+ }
494
+
495
+
434
496
  _EFFORT_LEVELS = frozenset({"low", "medium", "high", "xhigh", "max"})
435
497
 
436
498
 
@@ -468,19 +530,22 @@ def claude_thinking_cap_for(tier: str, dcfg: dict) -> int | None:
468
530
  return cap if cap >= 0 else None
469
531
 
470
532
 
471
- def claude_default_tier_key(scout: bool) -> str:
533
+ def claude_default_tier_key(scout: bool, write: bool = False) -> str:
472
534
  """Which config key names the default Claude tier.
473
535
 
474
536
  A scout defaults to its own key (medium): measured 2026-09-14, a Haiku
475
537
  scout took 28 turns and $0.24 to find one function that Sonnet found in
476
538
  8 turns for $0.046; the eval's canary case showed the same (15 turns
477
- against 2).
539
+ against 2). A write-mode worker has its own key too (see
540
+ docs/delegate-write.md for the measurement behind its default).
478
541
  """
542
+ if write:
543
+ return "claude_write_default_tier"
479
544
  return "claude_scout_default_tier" if scout else "claude_default_tier"
480
545
 
481
546
 
482
547
  def resolve_claude_tier(tier: str, model: str, dcfg: dict,
483
- scout: bool = False) -> tuple[str, str, str]:
548
+ scout: bool = False, write: bool = False) -> tuple[str, str, str]:
484
549
  """(tier, model_arg, error). An explicit ``model`` wins over the tier.
485
550
 
486
551
  ``model_arg`` empty means omit --model. ``error`` is non-empty when the
@@ -490,7 +555,7 @@ def resolve_claude_tier(tier: str, model: str, dcfg: dict,
490
555
  if not _MODEL_NAME_RE.match(model):
491
556
  return "", "", f"model name {model!r} is not a Claude model alias or id"
492
557
  return "custom", model, ""
493
- resolved = normalize_tier(tier, dcfg, claude_default_tier_key(scout))
558
+ resolved = normalize_tier(tier, dcfg, claude_default_tier_key(scout, write))
494
559
  if not resolved:
495
560
  return "", "", f"unknown tier {tier!r} (use one of {', '.join(CLAUDE_TIERS)})"
496
561
  table = {**CLAUDE_TIER_MODELS, **(dcfg.get("claude_tier_models") or {})}
@@ -502,7 +567,8 @@ def resolve_claude_tier(tier: str, model: str, dcfg: dict,
502
567
 
503
568
 
504
569
  def _claude_cmd(exe: str, model: str, system_prompt: str, *, effort: str = "",
505
- max_budget_usd: float = 0.0, settings: dict | None = None) -> list:
570
+ max_budget_usd: float = 0.0, settings: dict | None = None,
571
+ tools: str = SCOUT_TOOLS) -> list:
506
572
  """The locked-down headless argv. The prompt goes on stdin.
507
573
 
508
574
  --safe-mode: no CLAUDE.md, skills, plugins, hooks or MCP servers — but
@@ -515,12 +581,14 @@ def _claude_cmd(exe: str, model: str, system_prompt: str, *, effort: str = "",
515
581
  and Glob only, confined to the project, no user/project/local settings
516
582
  files, no CLAUDE.md, and the given --settings as the only permissions and
517
583
  hooks. --permission-mode dontAsk refuses anything not already allowed.
584
+ A write-mode worker is the same run with ``tools`` adding Edit and Write,
585
+ allowed only by the settings' Edit() rules.
518
586
  """
519
587
  cmd = [exe, "-p", "--output-format", "json", "--no-session-persistence"]
520
588
  if settings is None:
521
589
  cmd += ["--safe-mode", "--strict-mcp-config", "--tools", ""]
522
590
  else:
523
- cmd += ["--restricted", "--strict-mcp-config", "--tools", SCOUT_TOOLS,
591
+ cmd += ["--restricted", "--strict-mcp-config", "--tools", tools,
524
592
  "--permission-mode", "dontAsk", "--settings", json.dumps(settings)]
525
593
  cmd += ["--system-prompt", system_prompt]
526
594
  if model:
@@ -601,6 +669,8 @@ def parse_claude_json(stdout: str, requested_model: str = "") -> tuple[str, bool
601
669
  stats["model"] = primary
602
670
  if len(model_usage) > 1:
603
671
  stats["models_used"] = sorted(model_usage)
672
+ if isinstance(data.get("permission_denials"), list) and data["permission_denials"]:
673
+ stats["denials"] = data["permission_denials"]
604
674
  text = str(data.get("result") or "").strip()
605
675
  if data.get("is_error") or data.get("subtype") not in (None, "success"):
606
676
  reason = text or str(data.get("terminal_reason") or data.get("subtype") or "error")
@@ -614,7 +684,8 @@ def _run_claude(prompt: str, system_prompt: str, model: str = "", *,
614
684
  timeout: int = 120, effort: str = "",
615
685
  max_budget_usd: float = 0.0, scout_project: str | None = None,
616
686
  settings: dict | None = None,
617
- thinking_cap: int | None = None) -> tuple[str, bool, dict]:
687
+ thinking_cap: int | None = None,
688
+ tools: str = SCOUT_TOOLS) -> tuple[str, bool, dict]:
618
689
  """Run the locked-down ``claude -p``.
619
690
 
620
691
  Tool-less (default): in a throwaway directory. Scout (``scout_project``
@@ -630,7 +701,7 @@ def _run_claude(prompt: str, system_prompt: str, model: str = "", *,
630
701
  try:
631
702
  cmd = _claude_cmd(exe, model, system_prompt, effort=effort,
632
703
  max_budget_usd=max_budget_usd,
633
- settings=settings if scout else None)
704
+ settings=settings if scout else None, tools=tools)
634
705
  proc = subprocess.Popen(
635
706
  harden_win_argv(cmd),
636
707
  stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
@@ -808,6 +879,165 @@ def _handle_claude_delegate(task: str, task_type: str, context: str,
808
879
  return finalize("c3_delegate", meta, output, "ok")
809
880
 
810
881
 
882
+ def _worker_timeout(dcfg: dict) -> tuple[int, str]:
883
+ """(seconds, note). Inside the MCP client's own ceiling when it is known:
884
+ a worker the client abandons keeps writing with nobody to report to, so
885
+ C3's deadline must fire first and kill it."""
886
+ want = int(dcfg.get("claude_write_timeout", 600) or 600)
887
+ try:
888
+ from cli.tools.shell import _transport_ceiling_s
889
+ ceiling = _transport_ceiling_s()
890
+ except Exception:
891
+ ceiling = None
892
+ if ceiling and want > ceiling - 15:
893
+ return max(30, ceiling - 15), f"capped at {max(30, ceiling - 15)}s by the MCP client's {ceiling}s limit"
894
+ return want, ""
895
+
896
+
897
+ def _handle_claude_write(task: str, task_type: str, context: str, file_path: str,
898
+ svc, dcfg: dict, finalize, *, tier: str = "", model: str = "",
899
+ write_paths="") -> str:
900
+ """Write mode: a Claude worker makes a change the caller specified.
901
+
902
+ The caller names the write set; the worker runs ``claude -p --restricted``
903
+ with Read/Grep/Glob/Edit/Write under Access Guard (services/delegate_write
904
+ has the four fences). C3 then diffs every file the worker touched against
905
+ its pre-image, logs each change to the edit ledger, and returns the diff.
906
+ Never cached. A worker killed at its deadline still reports what it wrote.
907
+ """
908
+ from services import delegate_write as dw
909
+
910
+ base = {"task_type": task_type, "backend": "claude", "mode": "write"}
911
+ if not dcfg.get("claude_enabled", True):
912
+ return finalize("c3_delegate", base,
913
+ "[delegate:error] Claude not enabled. Set delegate.claude_enabled=true "
914
+ "in .c3/config.json", "disabled")
915
+ globs, problem = dw.parse_write_paths(write_paths)
916
+ if problem:
917
+ return finalize("c3_delegate", base, f"[delegate:error] {problem}", "error")
918
+ # A literal path the guard already refuses would only cost the worker
919
+ # turns and come back as a bare permission error: drop it now and say why.
920
+ guarded: list[str] = []
921
+ for g in list(globs):
922
+ if any(ch in g for ch in "*?["):
923
+ continue
924
+ full = Path(svc.project_path) / g
925
+ denial = access_guard.check(str(full), "write" if full.exists() else "create",
926
+ str(svc.project_path))
927
+ if denial:
928
+ globs.remove(g)
929
+ guarded.append(f"{g} ({denial.kind} rule '{denial.rule}')")
930
+ if not globs:
931
+ return finalize("c3_delegate", base,
932
+ "[delegate:blocked] Access Guard refuses every write path: "
933
+ + "; ".join(guarded) + ". Make those edits yourself.", "blocked")
934
+ resolved_tier, model_arg, problem = resolve_claude_tier(tier, model, dcfg, write=True)
935
+ if problem:
936
+ return finalize("c3_delegate", base, f"[delegate:error] {problem}", "error")
937
+ base["tier"] = resolved_tier
938
+ breaker = _backend_breaker("claude", dcfg)
939
+ if not breaker.allow():
940
+ return finalize("c3_delegate", base,
941
+ "[delegate:degraded] Claude skipped after repeated failures; retrying in "
942
+ f"~{breaker.cooldown_remaining()}s. Run 'claude --version' to diagnose.",
943
+ "degraded")
944
+
945
+ file_max = max(200, int(dcfg.get("claude_file_max_tokens", 8000) or 8000))
946
+ enriched = context or ""
947
+ packed = _pack_file_context(file_path, svc, file_max_tokens=file_max) if file_path else ""
948
+ if packed:
949
+ enriched = f"{enriched}\n\n{packed}" if enriched else packed
950
+ max_ctx = max(200, int(dcfg.get("claude_max_context_tokens", 24000) or 24000))
951
+ if count_tokens(enriched) > max_ctx:
952
+ enriched = enriched[:max_ctx * 4] + f"\n[context truncated at ~{max_ctx} tokens]"
953
+
954
+ try:
955
+ from cli.tools import _grants
956
+ session_id = _grants.session_id(svc)
957
+ except Exception:
958
+ session_id = ""
959
+ project = str(svc.project_path)
960
+ state = dw.new_state_dir(project, globs, session_id)
961
+ try:
962
+ try:
963
+ settings = worker_settings(project, globs, state)
964
+ except ValueError as exc:
965
+ return finalize("c3_delegate", base,
966
+ f"[delegate:blocked] write mode refused: {exc}. Fix the config "
967
+ "(c3 access list) or make the change yourself.", "blocked")
968
+ timeout, cap_note = _worker_timeout(dcfg)
969
+ effort = claude_effort_for(resolved_tier, dcfg)
970
+ if effort:
971
+ base["effort"] = effort
972
+ thinking_cap = claude_thinking_cap_for(resolved_tier, dcfg)
973
+ if thinking_cap is not None:
974
+ base["thinking_cap"] = thinking_cap
975
+ try:
976
+ budget = float(dcfg.get("claude_max_budget_usd") or 0.0)
977
+ except (TypeError, ValueError):
978
+ budget = 0.0
979
+ prompt = ("WRITE SET (the only paths you may edit or create, relative to the "
980
+ "repository root):\n" + "\n".join(f"- {g}" for g in globs)
981
+ + f"\n\nTIME: you are stopped after {timeout}s."
982
+ + f"\n\nTASK FROM THE LEAD AGENT:\n{task}"
983
+ + f"\n\nCONTEXT:\n{enriched or '(none)'}")
984
+ label = f"claude {resolved_tier} ({model_arg or 'cli-default'})"
985
+ _log_progress(svc, f"[delegate] {label} write mode, {len(globs)} write path(s)...")
986
+ t0 = time.monotonic()
987
+ output, ok, stats = _run_claude(prompt, _CLAUDE_WORKER_RULES, model_arg, timeout=timeout,
988
+ effort=effort, max_budget_usd=budget,
989
+ scout_project=project, settings=settings,
990
+ thinking_cap=thinking_cap, tools=dw.WORKER_TOOLS)
991
+ elapsed = round(time.monotonic() - t0, 1)
992
+ refused = dw.denial_lines(stats.pop("denials", None), project)
993
+ changes = dw.collect_changes(state, project)
994
+ finally:
995
+ shutil.rmtree(state, ignore_errors=True)
996
+
997
+ model_name = stats.pop("model", "") or model_arg or "cli-default"
998
+ added = sum(c["added"] for c in changes)
999
+ removed = sum(c["removed"] for c in changes)
1000
+ meta = {**base, "model": model_name, "elapsed": f"{elapsed}s", **stats,
1001
+ "files_changed": len(changes), "lines_added": added, "lines_removed": removed,
1002
+ "denied": len(refused)}
1003
+ if changes and getattr(svc, "edit_ledger", None):
1004
+ from cli.tools.edit import _log_to_ledger
1005
+ first_line = (task.strip().splitlines() or [""])[0][:100]
1006
+ for c in changes:
1007
+ _log_to_ledger(
1008
+ c["rel"], f"c3_delegate write ({model_name}): {first_line}",
1009
+ ["c3_delegate", "claude", resolved_tier], svc,
1010
+ detail={"delegate": {"backend": "claude", "tier": resolved_tier,
1011
+ "model": model_name, "added": c["added"],
1012
+ "removed": c["removed"]},
1013
+ "created": c["change"] == "created"})
1014
+
1015
+ n = len(changes)
1016
+ if ok:
1017
+ breaker.record_success()
1018
+ _delegate_metrics["total_calls"] += 1
1019
+ header = (f"[delegate:write] {label} changed {n} file(s) in {elapsed}s. Review the diff "
1020
+ "and run the tests before relying on it." if n else
1021
+ f"[delegate:write] {label} changed nothing in {elapsed}s.")
1022
+ status, report = "ok", output
1023
+ else:
1024
+ if breaker.record_failure():
1025
+ _notify_backend_degraded(svc, "claude", breaker)
1026
+ header = f"[delegate:write-failed] {output}"
1027
+ if n:
1028
+ header += f"\n{n} file(s) were changed before it stopped; review them:"
1029
+ status, report = "error", ""
1030
+ if cap_note and not ok:
1031
+ header += f" (deadline {cap_note})"
1032
+ if guarded:
1033
+ header += ("\nNot attempted, Access Guard refuses a delegate: " + "; ".join(guarded)
1034
+ + ". Make those edits yourself.")
1035
+ max_diff = max(2000, int(dcfg.get("claude_write_diff_max_chars", 24000) or 24000))
1036
+ text = dw.render(changes, header=header, report=report, refused=refused,
1037
+ max_diff_chars=max_diff)
1038
+ return finalize("c3_delegate", meta, text, status)
1039
+
1040
+
811
1041
  def check_gemini() -> dict:
812
1042
  """Zero-cost health check for Gemini CLI. Returns status dict."""
813
1043
  global _gemini_available
@@ -2116,7 +2346,8 @@ _OUTCOMES = frozenset({"ok", "cached", "error", "timeout", "blocked", "disabled"
2116
2346
  "unavailable", "degraded"})
2117
2347
  _CONFIDENCE = frozenset({"high", "medium", "low"})
2118
2348
  _USAGE_KEYS = ("input_tokens", "output_tokens", "cached_tokens", "cache_read_tokens",
2119
- "cache_write_tokens", "reasoning_tokens", "cost_usd", "turns")
2349
+ "cache_write_tokens", "reasoning_tokens", "cost_usd", "turns",
2350
+ "files_changed", "lines_added", "lines_removed", "denied")
2120
2351
 
2121
2352
 
2122
2353
  def _delegate_outcome(status) -> str:
@@ -2224,18 +2455,57 @@ def _ping_finalize(finalize):
2224
2455
  # Backends that can look things up in the project: claude as a guarded scout,
2225
2456
  # codex because its read-only sandbox already runs in the project.
2226
2457
  _SCOUT_BACKENDS = ("claude", "codex")
2458
+ _NOT_WRITE_TASKS = ("ping", "available", "codex_check", "gemini_check", "grok_check",
2459
+ "codex_resume")
2460
+
2461
+
2462
+ def _route_write(task: str, task_type: str, context: str, file_path: str, svc, finalize,
2463
+ backend: str, tier: str, model: str, route: dict, write_paths) -> str:
2464
+ """``write_paths`` given: write mode, which only the claude backend has.
2465
+
2466
+ It never cascades — a write lands on the backend the caller chose or
2467
+ nowhere — and it does not need allow_write_delegation: the worker runs
2468
+ under Access Guard in-band, unlike gemini or grok in write mode.
2469
+ """
2470
+ dcfg = svc.delegate_config or {}
2471
+ meta = {"task_type": task_type, "backend": backend, "mode": "write"}
2472
+ if not dcfg.get("enabled", True):
2473
+ return finalize("c3_delegate", meta, "[delegate:disabled]", "disabled")
2474
+ if task_type in _NOT_WRITE_TASKS:
2475
+ return finalize("c3_delegate", meta,
2476
+ f"[delegate:error] write_paths does not apply to task_type '{task_type}'.",
2477
+ "error")
2478
+ if backend == "host":
2479
+ provider, mapped = host_backend(svc)
2480
+ if mapped != "claude":
2481
+ return finalize("c3_delegate", meta,
2482
+ f"[delegate:error] write mode runs on the claude backend only; host "
2483
+ f"{provider or 'unknown'} maps to {mapped or 'nothing'}. "
2484
+ "Pass backend='claude' or make the change yourself.", "error")
2485
+ backend = "claude"
2486
+ if backend != "claude":
2487
+ return finalize("c3_delegate", {**meta, "backend": backend},
2488
+ f"[delegate:error] write mode runs on the claude backend only, not "
2489
+ f"'{backend}'.", "error")
2490
+ route["backend"] = "claude"
2491
+ if not model:
2492
+ route["tier"] = normalize_tier(tier, dcfg, claude_default_tier_key(False, write=True))
2493
+ return _handle_claude_write(task, task_type, context, file_path, svc, dcfg, finalize,
2494
+ tier=tier, model=model, write_paths=write_paths)
2227
2495
 
2228
2496
 
2229
2497
  def handle_delegate(task: str, task_type: str, context: str, file_path: str,
2230
2498
  svc, finalize, backend: str = "ollama",
2231
2499
  allow_write_delegation: bool = False,
2232
- tier: str = "", model: str = "", scout: bool = False) -> str:
2500
+ tier: str = "", model: str = "", scout: bool = False,
2501
+ write_paths: str = "") -> str:
2233
2502
  finalize = _telemetry_finalize(finalize, svc, requested_backend=backend,
2234
2503
  task_type=task_type)
2235
2504
  route = {"backend": backend}
2236
2505
  try:
2237
2506
  return _route_delegate(task, task_type, context, file_path, svc, finalize, backend,
2238
- allow_write_delegation, tier, model, scout, route)
2507
+ allow_write_delegation, tier, model, scout, route,
2508
+ write_paths=write_paths)
2239
2509
  except access_guard.AccessDenied as exc:
2240
2510
  # A guard refusal while packing file_path is the answer, not a crash:
2241
2511
  # the refusal text goes back as the response (the agent reads the
@@ -2249,8 +2519,12 @@ def handle_delegate(task: str, task_type: str, context: str, file_path: str,
2249
2519
 
2250
2520
  def _route_delegate(task: str, task_type: str, context: str, file_path: str,
2251
2521
  svc, finalize, backend: str, allow_write_delegation: bool,
2252
- tier: str, model: str, scout: bool, route: dict | None = None) -> str:
2522
+ tier: str, model: str, scout: bool, route: dict | None = None,
2523
+ write_paths: str = "") -> str:
2253
2524
  route = route if route is not None else {}
2525
+ if str(write_paths or "").strip():
2526
+ return _route_write(task, task_type, context, file_path, svc, finalize, backend,
2527
+ tier, model, route, write_paths)
2254
2528
  if task_type == "ping":
2255
2529
  finalize = _ping_finalize(finalize)
2256
2530
  task, context, file_path, task_type = PING_TASK, "", "", "ask"
@@ -127,8 +127,10 @@ def _budget_view(svc, detailed, finalize):
127
127
  calls = sum(r["calls"] for _k, r in used)
128
128
  ok = sum(r["outcomes"].get("ok", 0) + r["outcomes"].get("cached", 0) for _k, r in used)
129
129
  cost = sum(r.get("cost_usd") or 0.0 for _k, r in used)
130
+ files_changed = sum(r.get("files_changed") or 0 for _k, r in used)
130
131
  by = " | ".join(f"{k}:{r['calls']}" for k, r in used[:4])
131
- lines.append(f"[delegate:7d] {calls} calls, {ok} answered, ${cost:.4f} reported ({by})")
132
+ written = f", {files_changed} file(s) written" if files_changed > 0 else ""
133
+ lines.append(f"[delegate:7d] {calls} calls, {ok} answered, ${cost:.4f} reported{written} ({by})")
132
134
  except Exception:
133
135
  pass
134
136
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.138.0
3
+ Version: 2.139.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