code-context-control 2.111.0__tar.gz → 2.112.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 (489) hide show
  1. {code_context_control-2.111.0 → code_context_control-2.112.0}/PKG-INFO +1 -1
  2. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/c3.py +1 -1
  3. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/mcp_server.py +20 -5
  4. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/shell.py +292 -51
  5. code_context_control-2.112.0/cli/tools/shell_render.py +351 -0
  6. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/PKG-INFO +1 -1
  7. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/SOURCES.txt +4 -0
  8. {code_context_control-2.111.0 → code_context_control-2.112.0}/pyproject.toml +1 -1
  9. code_context_control-2.112.0/services/shell_output.py +1051 -0
  10. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_c3_shell.py +14 -1
  11. code_context_control-2.112.0/tests/test_shell_budget_s1.py +317 -0
  12. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_eval.py +2 -1
  13. code_context_control-2.112.0/tests/test_shell_output.py +727 -0
  14. {code_context_control-2.111.0 → code_context_control-2.112.0}/LICENSE +0 -0
  15. {code_context_control-2.111.0 → code_context_control-2.112.0}/README.md +0 -0
  16. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/__init__.py +0 -0
  17. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/_hook_utils.py +0 -0
  18. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/_shell_writes.py +0 -0
  19. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/commands/__init__.py +0 -0
  20. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/commands/common.py +0 -0
  21. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/commands/parser.py +0 -0
  22. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/docs.html +0 -0
  23. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/edits.html +0 -0
  24. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/access.html +0 -0
  25. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/bitbucket.html +0 -0
  26. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/credentials.html +0 -0
  27. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/getting-started.html +0 -0
  28. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/index.html +0 -0
  29. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/masking.html +0 -0
  30. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/oracle.html +0 -0
  31. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/shared.css +0 -0
  32. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/tools.html +0 -0
  33. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/guide/workflow.html +0 -0
  34. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_access_guard.py +0 -0
  35. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_artifact.py +0 -0
  36. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_auto_snapshot.py +0 -0
  37. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_c3_signal.py +0 -0
  38. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_c3read.py +0 -0
  39. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_dispatch.py +0 -0
  40. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_edit_ledger.py +0 -0
  41. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_edit_unlock.py +0 -0
  42. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_filter.py +0 -0
  43. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_ghost_files.py +0 -0
  44. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_pretool_enforce.py +0 -0
  45. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_prompt_recall.py +0 -0
  46. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_read.py +0 -0
  47. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_session_stats.py +0 -0
  48. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hook_terse_advisor.py +0 -0
  49. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_server.py +0 -0
  50. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/app.js +0 -0
  51. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/add_project.js +0 -0
  52. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/config_editor.js +0 -0
  53. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  54. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_health.js +0 -0
  55. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_panel.js +0 -0
  56. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  57. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  58. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/drill_views.js +0 -0
  59. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/global_search.js +0 -0
  60. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_access.js +0 -0
  61. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_ci.js +0 -0
  62. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  63. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  64. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  65. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_locks.js +0 -0
  66. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  67. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  68. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/modals.js +0 -0
  69. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/project_card.js +0 -0
  70. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/project_tree.js +0 -0
  71. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/session_drawer.js +0 -0
  72. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/settings_modal.js +0 -0
  73. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/sidebar.js +0 -0
  74. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/summary_bar.js +0 -0
  75. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/task_board.js +0 -0
  76. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/toasts.js +0 -0
  77. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/components/topbar.js +0 -0
  78. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui/state.js +0 -0
  79. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/hub_ui.html +0 -0
  80. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/mcp_proxy.py +0 -0
  81. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/progress.py +0 -0
  82. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/server.py +0 -0
  83. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/__init__.py +0 -0
  84. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/_grants.py +0 -0
  85. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/_helpers.py +0 -0
  86. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/agent.py +0 -0
  87. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/artifacts.py +0 -0
  88. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/bitbucket.py +0 -0
  89. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/ci.py +0 -0
  90. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/compress.py +0 -0
  91. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/credentials.py +0 -0
  92. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/delegate.py +0 -0
  93. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/edit.py +0 -0
  94. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/edit_verify.py +0 -0
  95. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/edits.py +0 -0
  96. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/federate.py +0 -0
  97. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/filter.py +0 -0
  98. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/impact.py +0 -0
  99. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/jira.py +0 -0
  100. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/locks.py +0 -0
  101. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/memory.py +0 -0
  102. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/override.py +0 -0
  103. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/project.py +0 -0
  104. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/read.py +0 -0
  105. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/search.py +0 -0
  106. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/session.py +0 -0
  107. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/status.py +0 -0
  108. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/tasks.py +0 -0
  109. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/tools/validate.py +0 -0
  110. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/api.js +0 -0
  111. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/app.js +0 -0
  112. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/access.js +0 -0
  113. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/bitbucket.js +0 -0
  114. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/chat.js +0 -0
  115. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/credentials.js +0 -0
  116. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/dashboard.js +0 -0
  117. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/edits.js +0 -0
  118. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/enforcement.js +0 -0
  119. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/instructions.js +0 -0
  120. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/jira.js +0 -0
  121. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/memory.js +0 -0
  122. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/sessions.js +0 -0
  123. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/settings.js +0 -0
  124. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/sidebar.js +0 -0
  125. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/components/tasks.js +0 -0
  126. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/icons.js +0 -0
  127. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/pm_shared.js +0 -0
  128. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/shared.js +0 -0
  129. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui/theme.js +0 -0
  130. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui.html +0 -0
  131. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui_legacy.html +0 -0
  132. {code_context_control-2.111.0 → code_context_control-2.112.0}/cli/ui_nano.html +0 -0
  133. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  134. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/entry_points.txt +0 -0
  135. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/requires.txt +0 -0
  136. {code_context_control-2.111.0 → code_context_control-2.112.0}/code_context_control.egg-info/top_level.txt +0 -0
  137. {code_context_control-2.111.0 → code_context_control-2.112.0}/core/__init__.py +0 -0
  138. {code_context_control-2.111.0 → code_context_control-2.112.0}/core/config.py +0 -0
  139. {code_context_control-2.111.0 → code_context_control-2.112.0}/core/ide.py +0 -0
  140. {code_context_control-2.111.0 → code_context_control-2.112.0}/core/mcp_toml.py +0 -0
  141. {code_context_control-2.111.0 → code_context_control-2.112.0}/core/web_security.py +0 -0
  142. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/__init__.py +0 -0
  143. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/config.py +0 -0
  144. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/mcp_oracle.py +0 -0
  145. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/oracle_server.py +0 -0
  146. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/oracle_ui.html +0 -0
  147. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/__init__.py +0 -0
  148. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/activity_reporter.py +0 -0
  149. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/api_auth.py +0 -0
  150. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/c3_bridge.py +0 -0
  151. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/chat_engine.py +0 -0
  152. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/chat_poll.py +0 -0
  153. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/chat_store.py +0 -0
  154. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/cross_memory.py +0 -0
  155. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/discovery_audit.py +0 -0
  156. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/federated_graph.py +0 -0
  157. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/health_checker.py +0 -0
  158. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/insight_engine.py +0 -0
  159. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/local_session.py +0 -0
  160. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/memory_reader.py +0 -0
  161. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/memory_writer.py +0 -0
  162. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/mobile_api.py +0 -0
  163. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/project_scanner.py +0 -0
  164. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/review_agent.py +0 -0
  165. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/tool_executor.py +0 -0
  166. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/services/tool_registry.py +0 -0
  167. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/activity.js +0 -0
  168. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/agents.js +0 -0
  169. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/app.js +0 -0
  170. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/busy.js +0 -0
  171. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/conversations.js +0 -0
  172. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/input.js +0 -0
  173. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/markdown.js +0 -0
  174. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/send.js +0 -0
  175. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/stream_renderer.js +0 -0
  176. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/chat/toolbar.js +0 -0
  177. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/core.js +0 -0
  178. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/crossgraph.js +0 -0
  179. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/header.js +0 -0
  180. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/insights.js +0 -0
  181. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/projects.js +0 -0
  182. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/qrcode.js +0 -0
  183. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/settings.js +0 -0
  184. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/suggestions.js +0 -0
  185. {code_context_control-2.111.0 → code_context_control-2.112.0}/oracle/ui/theme_tabs.js +0 -0
  186. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/__init__.py +0 -0
  187. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/access_guard.py +0 -0
  188. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/access_telemetry.py +0 -0
  189. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/activity_log.py +0 -0
  190. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/agent_base.py +0 -0
  191. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/agent_locks.py +0 -0
  192. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/agents.py +0 -0
  193. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/artifact_defs.py +0 -0
  194. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/artifact_store.py +0 -0
  195. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/auto_memory.py +0 -0
  196. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/background_service.py +0 -0
  197. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/__init__.py +0 -0
  198. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/external/__init__.py +0 -0
  199. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/external/aider_polyglot.py +0 -0
  200. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/external/swe_bench.py +0 -0
  201. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/search_eval.py +0 -0
  202. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bench/shell_eval.py +0 -0
  203. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/benchmark_dashboard.py +0 -0
  204. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bitbucket_client.py +0 -0
  205. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/bitbucket_credentials.py +0 -0
  206. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_act.py +0 -0
  207. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_cache.py +0 -0
  208. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_expr.py +0 -0
  209. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_failures.py +0 -0
  210. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_github.py +0 -0
  211. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_impact.py +0 -0
  212. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_intel.py +0 -0
  213. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_runner.py +0 -0
  214. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ci_workflow.py +0 -0
  215. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/circuit_breaker.py +0 -0
  216. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/claude_md.py +0 -0
  217. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/compressor.py +0 -0
  218. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/context_snapshot.py +0 -0
  219. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/conversation_store.py +0 -0
  220. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/cred_audit.py +0 -0
  221. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/cred_telemetry.py +0 -0
  222. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/credential_store.py +0 -0
  223. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/doc_index.py +0 -0
  224. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/e2e_benchmark.py +0 -0
  225. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/e2e_evaluator.py +0 -0
  226. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/e2e_tasks.py +0 -0
  227. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/edit_ledger.py +0 -0
  228. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/embedding_index.py +0 -0
  229. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/enforcement_policy.py +0 -0
  230. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/error_reporting.py +0 -0
  231. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/file_memory.py +0 -0
  232. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/git_context.py +0 -0
  233. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/hub_service.py +0 -0
  234. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/index_store.py +0 -0
  235. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/indexer.py +0 -0
  236. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/jira_client.py +0 -0
  237. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/jira_cloud.py +0 -0
  238. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/jira_credentials.py +0 -0
  239. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/jira_data_center.py +0 -0
  240. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/jira_links.py +0 -0
  241. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/lexical_index.py +0 -0
  242. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/mask_activation.py +0 -0
  243. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/mask_mirror.py +0 -0
  244. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/mask_presets.py +0 -0
  245. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory.py +0 -0
  246. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_consolidator.py +0 -0
  247. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_distiller.py +0 -0
  248. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_graph.py +0 -0
  249. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_grounder.py +0 -0
  250. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_queue.py +0 -0
  251. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/memory_scorer.py +0 -0
  252. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/metrics.py +0 -0
  253. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/notifications.py +0 -0
  254. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ollama_bridge.py +0 -0
  255. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ollama_client.py +0 -0
  256. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/ollama_credentials.py +0 -0
  257. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/oracle_service.py +0 -0
  258. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/output_filter.py +0 -0
  259. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/override_grants.py +0 -0
  260. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/override_policy.py +0 -0
  261. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/override_requests.py +0 -0
  262. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/override_wake.py +0 -0
  263. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/parser.py +0 -0
  264. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/project_manager.py +0 -0
  265. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/project_runtime.py +0 -0
  266. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/protocol.py +0 -0
  267. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/proxy_state.py +0 -0
  268. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/repo_map.py +0 -0
  269. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/repo_shape.py +0 -0
  270. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/reranker.py +0 -0
  271. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/retention.py +0 -0
  272. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/retrieval.py +0 -0
  273. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/retrieval_broker.py +0 -0
  274. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/router.py +0 -0
  275. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/runtime.py +0 -0
  276. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/scanner.py +0 -0
  277. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/session_benchmark.py +0 -0
  278. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/session_manager.py +0 -0
  279. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/session_preloader.py +0 -0
  280. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/subprojects.py +0 -0
  281. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/task_store.py +0 -0
  282. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/telemetry.py +0 -0
  283. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/text_index.py +0 -0
  284. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/time_tracker.py +0 -0
  285. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/tool_classifier.py +0 -0
  286. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/transcript_index.py +0 -0
  287. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/validation_cache.py +0 -0
  288. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/vector_store.py +0 -0
  289. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/watcher.py +0 -0
  290. {code_context_control-2.111.0 → code_context_control-2.112.0}/services/win_subprocess.py +0 -0
  291. {code_context_control-2.111.0 → code_context_control-2.112.0}/setup.cfg +0 -0
  292. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_builtin_optout.py +0 -0
  293. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_guard.py +0 -0
  294. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_guard_meta.py +0 -0
  295. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_guard_shell_project.py +0 -0
  296. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_guard_surfaces.py +0 -0
  297. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_access_guard_wiring.py +0 -0
  298. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_activity_reporter.py +0 -0
  299. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_agent_locks.py +0 -0
  300. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_aider_polyglot.py +0 -0
  301. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_antigravity_transition.py +0 -0
  302. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_artifact_store.py +0 -0
  303. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_artifact_tool.py +0 -0
  304. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_background_service.py +0 -0
  305. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  306. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_bitbucket_client.py +0 -0
  307. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_bitbucket_config_fallback.py +0 -0
  308. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_bitbucket_credentials.py +0 -0
  309. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_bitbucket_tool.py +0 -0
  310. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_builtin_modes.py +0 -0
  311. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  312. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_chat_poll.py +0 -0
  313. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_act.py +0 -0
  314. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_cache.py +0 -0
  315. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_expr.py +0 -0
  316. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_impact.py +0 -0
  317. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_intel.py +0 -0
  318. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_runner.py +0 -0
  319. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ci_workflow.py +0 -0
  320. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_circuit_breaker.py +0 -0
  321. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_claude_md_merge.py +0 -0
  322. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_claude_md_updater.py +0 -0
  323. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_cli_smoke.py +0 -0
  324. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_compressor_large_file.py +0 -0
  325. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_confirm_guard.py +0 -0
  326. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_confirm_guard_wiring.py +0 -0
  327. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_cooccurrence_bounds.py +0 -0
  328. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_cred_audit.py +0 -0
  329. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_cred_telemetry.py +0 -0
  330. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_credential_login_kind.py +0 -0
  331. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_credential_store.py +0 -0
  332. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_credential_ui_parity.py +0 -0
  333. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_credentials_routes.py +0 -0
  334. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_credentials_tool.py +0 -0
  335. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_creds_cli_smoke.py +0 -0
  336. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_delegate_cascade.py +0 -0
  337. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_delegate_version_probe.py +0 -0
  338. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_e2e_benchmark.py +0 -0
  339. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_edit_ledger_deadline.py +0 -0
  340. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_edit_ledger_hook.py +0 -0
  341. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_edit_locking.py +0 -0
  342. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_edit_normalization.py +0 -0
  343. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_edit_verify.py +0 -0
  344. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_embedding_index_deadlock.py +0 -0
  345. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_enforcement_flip.py +0 -0
  346. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_enforcement_policy.py +0 -0
  347. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_enforcement_routes.py +0 -0
  348. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_env_parser.py +0 -0
  349. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  350. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_federated_graph.py +0 -0
  351. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_filter_backoff.py +0 -0
  352. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ghost_files.py +0 -0
  353. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ghost_generation.py +0 -0
  354. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_git_branch_awareness.py +0 -0
  355. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_access_guard.py +0 -0
  356. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_codex_compat.py +0 -0
  357. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_dispatch.py +0 -0
  358. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_pretool_enforce.py +0 -0
  359. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_prompt_recall.py +0 -0
  360. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_smoke.py +0 -0
  361. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_state.py +0 -0
  362. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hook_utf8_stdio.py +0 -0
  363. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_credentials_route.py +0 -0
  364. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_credentials_routes_write.py +0 -0
  365. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_enforcement_routes.py +0 -0
  366. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_inspect_api.py +0 -0
  367. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_locks_routes.py +0 -0
  368. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_oracle_service_routes.py +0 -0
  369. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_override_routes.py +0 -0
  370. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_server_smoke.py +0 -0
  371. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_hub_subproject_links.py +0 -0
  372. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ide_workflow_adaptation.py +0 -0
  373. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  374. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  375. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_init_scan.py +0 -0
  376. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_install_mcp_entrypoint.py +0 -0
  377. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_cli_smoke.py +0 -0
  378. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_client.py +0 -0
  379. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_config_fallback.py +0 -0
  380. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_credentials.py +0 -0
  381. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_links.py +0 -0
  382. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_routes.py +0 -0
  383. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_jira_tool.py +0 -0
  384. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_lazy_store_init.py +0 -0
  385. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mask_guard.py +0 -0
  386. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mask_routes.py +0 -0
  387. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mask_surfaces.py +0 -0
  388. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mcp_host_guard.py +0 -0
  389. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mcp_memory_timeout.py +0 -0
  390. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mcp_server_smoke.py +0 -0
  391. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mcp_toml.py +0 -0
  392. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_memory_distiller.py +0 -0
  393. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_memory_graph_api.py +0 -0
  394. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_memory_queue.py +0 -0
  395. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_memory_system.py +0 -0
  396. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mobile_api.py +0 -0
  397. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mobile_extras.py +0 -0
  398. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mobile_override_routes.py +0 -0
  399. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_mobile_security_api.py +0 -0
  400. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_no_stale_model_pins.py +0 -0
  401. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_notification_dedup.py +0 -0
  402. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_notification_discipline.py +0 -0
  403. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ollama_credentials.py +0 -0
  404. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_api_auth.py +0 -0
  405. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_apikey_api.py +0 -0
  406. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_c3_bridge.py +0 -0
  407. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_chat_engine.py +0 -0
  408. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_chat_store.py +0 -0
  409. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_discovery_api.py +0 -0
  410. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_discovery_audit.py +0 -0
  411. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_health_probe.py +0 -0
  412. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_local_auth.py +0 -0
  413. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_ollama_bridge.py +0 -0
  414. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_scanner_cache.py +0 -0
  415. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_security_fixes.py +0 -0
  416. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_session_bootstrap.py +0 -0
  417. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_subproject_awareness.py +0 -0
  418. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_oracle_ui_bundle.py +0 -0
  419. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_output_filter.py +0 -0
  420. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_override_grants.py +0 -0
  421. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_override_grants_mcp.py +0 -0
  422. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_override_requests.py +0 -0
  423. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_override_wake.py +0 -0
  424. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_permissions.py +0 -0
  425. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_pm_api.py +0 -0
  426. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_project_manager.py +0 -0
  427. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_project_manager_merge.py +0 -0
  428. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_project_tool.py +0 -0
  429. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_read_coercion.py +0 -0
  430. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_read_edit_parity.py +0 -0
  431. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_related_facts.py +0 -0
  432. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_repo_map.py +0 -0
  433. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_repo_shape.py +0 -0
  434. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_response_boilerplate.py +0 -0
  435. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_retention.py +0 -0
  436. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_review_digest.py +0 -0
  437. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_eval.py +0 -0
  438. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p1.py +0 -0
  439. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p2.py +0 -0
  440. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p2b.py +0 -0
  441. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p3.py +0 -0
  442. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p4.py +0 -0
  443. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_search_p5.py +0 -0
  444. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_service_durability.py +0 -0
  445. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_session_benchmark.py +0 -0
  446. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_session_budget.py +0 -0
  447. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_creds.py +0 -0
  448. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_robustness.py +0 -0
  449. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_telemetry_s0.py +0 -0
  450. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_write_ledger.py +0 -0
  451. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_shell_writes.py +0 -0
  452. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subproject_clear_scope.py +0 -0
  453. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subproject_depth_consumers.py +0 -0
  454. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subproject_exclusion.py +0 -0
  455. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subproject_federation.py +0 -0
  456. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subproject_linkage_api.py +0 -0
  457. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_subprojects.py +0 -0
  458. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_swe_bench.py +0 -0
  459. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_task_store.py +0 -0
  460. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_task_tool.py +0 -0
  461. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_time_tracker.py +0 -0
  462. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_token_telemetry.py +0 -0
  463. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_token_tracking.py +0 -0
  464. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_tool_registry.py +0 -0
  465. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_ui_jsx_syntax.py +0 -0
  466. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_upgrade_and_version.py +0 -0
  467. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_validate.py +0 -0
  468. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_vault_write_guard.py +0 -0
  469. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_version_sync.py +0 -0
  470. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_web_security.py +0 -0
  471. {code_context_control-2.111.0 → code_context_control-2.112.0}/tests/test_windows_reliability.py +0 -0
  472. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/__init__.py +0 -0
  473. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/backend.py +0 -0
  474. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/main.py +0 -0
  475. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/__init__.py +0 -0
  476. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/benchmark_view.py +0 -0
  477. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/claudemd_view.py +0 -0
  478. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/compress_view.py +0 -0
  479. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/index_view.py +0 -0
  480. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/init_view.py +0 -0
  481. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/mcp_view.py +0 -0
  482. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/optimize_view.py +0 -0
  483. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/pipe_view.py +0 -0
  484. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/projects_view.py +0 -0
  485. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/search_view.py +0 -0
  486. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/session_view.py +0 -0
  487. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/stats.py +0 -0
  488. {code_context_control-2.111.0 → code_context_control-2.112.0}/tui/screens/ui_view.py +0 -0
  489. {code_context_control-2.111.0 → code_context_control-2.112.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.111.0
3
+ Version: 2.112.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
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
92
92
  # Config
93
93
  CONFIG_DIR = ".c3"
94
94
  CONFIG_FILE = ".c3/config.json"
95
- __version__ = "2.111.0"
95
+ __version__ = "2.112.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -883,16 +883,28 @@ async def c3_impact(target: str, file_path: str = "", mode: str = "symbol",
883
883
 
884
884
 
885
885
  @mcp.tool()
886
- async def c3_shell(cmd: str, cwd: str = "", timeout: int = 60,
886
+ async def c3_shell(cmd: str = "", cwd: str = "", timeout: int = 60,
887
887
  filter_output: bool = True, log: bool = True,
888
888
  env_creds: str = "",
889
+ output_id: str = "", output_action: str = "",
890
+ pattern: str = "", lines: str = "", stream: str = "stdout",
891
+ max_bytes: int = 0,
889
892
  ctx: Context = None) -> str:
890
- """EXECUTE shell command — structured returns, auto-filter, ledger-aware.
893
+ """EXECUTE shell command — structured returns, budgeted output, ledger-aware.
891
894
  Use for tests, git, build, scripts. Returns exit_code/stdout/stderr/duration_ms.
892
- Auto-filters stdout >30 lines; auto-logs git mutations to the edit ledger.
895
+ The response never exceeds 18 KiB (max_bytes may only lower it): a stream that does
896
+ not fit is clipped line by line and windowed head/tail with a note naming an
897
+ output_id; the raw streams are kept for 3 days OUTSIDE the project. Page them back
898
+ with output_id='o-…' and output_action='read' (lines='120-180'), 'search'
899
+ (pattern=regex), 'tail' (lines='80') or 'delete', stream='stdout'|'stderr'; an id
900
+ resolves only for this project and this session under the current access rules.
901
+ The echoed command is its first line; a multi-line command shows (N lines, M chars,
902
+ sha256). Auto-filters stdout >30 lines when it fits; auto-logs git mutations to
903
+ the edit ledger.
893
904
  Credentials: env_creds='NAME1,NAME2' injects vault entries as env vars, and
894
905
  {{cred:NAME}} inside cmd expands server-side — decoded values never enter
895
- model context (see c3_credentials; echoed values are auto-redacted).
906
+ model context (see c3_credentials; echoed values are auto-redacted, in the
907
+ response and in the kept output).
896
908
  Best-effort block of catastrophic commands (rm -rf of /, a top-level system dir, or
897
909
  $HOME/~; fork bombs; whole-drive wipes) — a guard, NOT a sandbox. Soft-warns on
898
910
  --force, --no-verify, reset --hard.
@@ -904,7 +916,10 @@ async def c3_shell(cmd: str, cwd: str = "", timeout: int = 60,
904
916
  return _finalize_response(ctx, name, args, resp, summ, **kw)
905
917
 
906
918
  return await handle_shell(cmd, cwd, timeout, filter_output, log, svc, finalize,
907
- env_creds=env_creds)
919
+ env_creds=env_creds, output_id=output_id,
920
+ output_action=output_action, pattern=pattern,
921
+ lines=lines or None, stream=stream or "stdout",
922
+ max_bytes=max_bytes or None)
908
923
 
909
924
 
910
925
  @mcp.tool()
@@ -29,8 +29,22 @@ from cli._shell_writes import shell_write_targets
29
29
  from cli.tools import _grants
30
30
  from cli.tools._helpers import finalize_with_tokens
31
31
  from cli.tools.filter import handle_filter
32
+ from cli.tools.shell_render import (
33
+ allocate,
34
+ cmd_display,
35
+ effective_budget,
36
+ grep_pattern,
37
+ human_bytes,
38
+ shape_stream,
39
+ )
32
40
  from core import count_tokens
33
41
  from services import access_guard
42
+ from services.shell_output import (
43
+ TEXT_MAX_BYTES,
44
+ OutputAccessError,
45
+ ShellCapture,
46
+ ShellOutputStore,
47
+ )
34
48
 
35
49
  # Commands that mutate repo state — trigger edit-ledger refresh after success.
36
50
  _GIT_MUTATING = re.compile(
@@ -211,9 +225,28 @@ def _kill_tree(proc: subprocess.Popen) -> None:
211
225
  pass
212
226
 
213
227
 
228
+ def _default_redact(text: str) -> str:
229
+ """Scrub vault values from a piece of output; never raises."""
230
+ try:
231
+ from services import credential_store as _creds
232
+ return _creds.redact_text(text)
233
+ except Exception:
234
+ return text
235
+
236
+
214
237
  def _run_sync(cmd: str, cwd: str, timeout: int,
215
- extra_env: dict | None = None) -> dict:
216
- """Blocking subprocess run with hard kill on timeout. Returns structured dict."""
238
+ extra_env: dict | None = None, *, redact=_default_redact,
239
+ spool_dir=None) -> dict:
240
+ """Blocking subprocess run with hard kill on timeout. Returns structured dict.
241
+
242
+ Since 2.112.0 the child's streams are not buffered in memory: a
243
+ ShellCapture pumps them to spool files with bounded head/tail previews
244
+ (services/shell_output.py). ``stdout``/``stderr`` in the result carry the
245
+ whole text when the stream is at most TEXT_MAX_BYTES and None otherwise;
246
+ ``capture`` carries the stats and previews either way. Callers that
247
+ patch this function with a plain dict (tests) still work: no ``capture``
248
+ means "small output, text present".
249
+ """
217
250
  start = time.time()
218
251
  bash = _select_bash()
219
252
  if bash:
@@ -229,7 +262,6 @@ def _run_sync(cmd: str, cwd: str, timeout: int,
229
262
  proc = subprocess.Popen(
230
263
  popen_target, shell=use_shell, cwd=cwd,
231
264
  stdout=subprocess.PIPE, stderr=subprocess.PIPE,
232
- text=True, encoding="utf-8", errors="replace",
233
265
  **_popen_kwargs(extra_env),
234
266
  )
235
267
  except (OSError, ValueError) as exc:
@@ -241,24 +273,27 @@ def _run_sync(cmd: str, cwd: str, timeout: int,
241
273
  "timed_out": False,
242
274
  "shell": shell_name,
243
275
  }
244
- timed_out = False
245
- try:
246
- stdout, stderr = proc.communicate(timeout=timeout)
247
- except subprocess.TimeoutExpired:
248
- _kill_tree(proc)
276
+ if spool_dir is None:
277
+ spool_dir = ShellOutputStore().spool_dir()
278
+ capture = ShellCapture(proc, spool_dir, redact=redact, kill_tree=_kill_tree)
279
+ timed_out = capture.wait(timeout)
280
+
281
+ def _text(stream: str):
282
+ if getattr(capture.stats, stream).bytes > TEXT_MAX_BYTES:
283
+ return None
249
284
  try:
250
- stdout, stderr = proc.communicate(timeout=2)
285
+ return capture.text(stream)
251
286
  except Exception:
252
- stdout, stderr = "", ""
253
- timed_out = True
287
+ return None
254
288
 
255
289
  return {
256
290
  "exit_code": -1 if timed_out else (proc.returncode or 0),
257
- "stdout": stdout or "",
258
- "stderr": stderr or "",
259
- "duration_ms": round((time.time() - start) * 1000),
291
+ "stdout": _text("stdout"),
292
+ "stderr": _text("stderr"),
293
+ "duration_ms": capture.duration_ms or round((time.time() - start) * 1000),
260
294
  "timed_out": timed_out,
261
295
  "shell": shell_name,
296
+ "capture": capture,
262
297
  }
263
298
 
264
299
 
@@ -428,54 +463,121 @@ def _longest_line(*texts: str) -> int:
428
463
  return longest
429
464
 
430
465
 
466
+ def _stream_facts(result: dict, stream: str) -> dict:
467
+ """Size, line count, longest line and available text/previews of one stream."""
468
+ text = result.get(stream)
469
+ capture = result.get("capture")
470
+ if capture is not None:
471
+ st = getattr(capture.stats, stream)
472
+ return {"text": text, "head": st.head, "tail": st.tail, "bytes": st.bytes,
473
+ "lines": st.lines, "longest": st.longest_line}
474
+ text = text or ""
475
+ return {"text": text, "head": "", "tail": "", "bytes": len(text.encode("utf-8", errors="replace")),
476
+ "lines": text.count("\n") + (1 if text and not text.endswith("\n") else 0),
477
+ "longest": _longest_line(text)}
478
+
479
+
431
480
  def render_shell_response(cmd: str, result: dict, svc, *,
432
481
  filter_output: bool = True, warn: str = "",
433
482
  capped_note: str = "", touched_files=(),
434
- cred_names=(), swept_ghosts=()) -> tuple[str, dict]:
483
+ cred_names=(), swept_ghosts=(),
484
+ max_bytes: int | None = None) -> tuple[str, dict]:
435
485
  """Turn a finished command into the body the agent reads, plus its stats.
436
486
 
437
487
  Pure with respect to the subprocess: ``result`` is the dict _run_sync
438
- returns (exit_code, stdout, stderr, duration_ms, timed_out, shell), so
439
- the shell-eval harness (services/bench/shell_eval.py) can feed captured
440
- or synthetic streams through exactly the code path a live call uses.
441
- Runs the same auto-filter handle_shell always ran — only stdout, only when
442
- it has more than _FILTER_THRESHOLD_LINES newlines, never for a git
443
- diagnostic — and assembles the same sections in the same order.
488
+ returns (exit_code, stdout, stderr, duration_ms, timed_out, shell, and
489
+ since 2.112.0 an optional ``capture`` with stats and head/tail previews
490
+ for a stream too large to hold), so the shell-eval harness
491
+ (services/bench/shell_eval.py) can feed captured or synthetic streams
492
+ through exactly the code path a live call uses.
493
+
494
+ Budget (2.112.0, cli/tools/shell_render.py): the rendered body never
495
+ exceeds ``effective_budget()`` bytes — 18 KiB by default, 22 KiB ceiling,
496
+ ``max_bytes`` and ``hybrid.shell_budget_bytes`` may only lower it,
497
+ ``filter_output=False`` never lifts it. A stream that fits its share
498
+ passes through untouched (and, when it has more than
499
+ _FILTER_THRESHOLD_LINES newlines and is not a git diagnostic, through the
500
+ same auto-filter as before); a stream that does not is clipped line by
501
+ line and windowed head/tail with an omission note naming the output id.
502
+ Whenever anything was dropped — clipped, windowed or filtered — the raw
503
+ streams are kept in the spill store and ``stats["needs_spill"]`` tells
504
+ handle_shell to promote them.
444
505
 
445
506
  ``stats`` is what telemetry records about the call (see
446
507
  SessionManager.record_tool_tokens ``detail``): stdout_bytes and
447
- stderr_bytes are measured BEFORE filtering so a later budget phase can
448
- size the band it changes; longest_line is the tell for the single-line
449
- monsters (minified bundles, JSONL) that a newline-count trigger never
450
- sees; filtered/spilled/output_id describe what this renderer did.
508
+ stderr_bytes are measured BEFORE filtering; longest_line is the tell for
509
+ the single-line monsters (minified bundles, JSONL) that a newline-count
510
+ trigger never sees; filtered/spilled/output_id describe what this
511
+ renderer did.
451
512
  """
452
- raw_stdout = result.get("stdout") or ""
453
- raw_stderr = result.get("stderr") or ""
513
+ out = _stream_facts(result, "stdout")
514
+ err = _stream_facts(result, "stderr")
515
+ capture = result.get("capture")
454
516
  stats: dict = {
455
517
  "exit_code": result.get("exit_code"),
456
518
  "timed_out": bool(result.get("timed_out")),
457
- "stdout_bytes": len(raw_stdout.encode("utf-8", errors="replace")),
458
- "stderr_bytes": len(raw_stderr.encode("utf-8", errors="replace")),
459
- "longest_line": _longest_line(raw_stdout, raw_stderr),
519
+ "stdout_bytes": out["bytes"],
520
+ "stderr_bytes": err["bytes"],
521
+ "longest_line": max(out["longest"], err["longest"]),
460
522
  "filtered": False,
461
523
  "spilled": False,
462
524
  "output_id": None,
463
525
  "cmd_class": _cmd_class(cmd),
526
+ "needs_spill": False,
464
527
  }
465
528
 
466
- stdout = raw_stdout
529
+ config_default = None
530
+ try:
531
+ config_default = (getattr(svc, "hybrid_config", None) or {}).get("shell_budget_bytes")
532
+ except Exception:
533
+ config_default = None
534
+ budget = effective_budget(max_bytes, config_default=config_default)
535
+ out_alloc, err_alloc = allocate(budget, out["bytes"], err["bytes"])
536
+ stats["budget_bytes"] = budget
537
+ output_id = getattr(capture, "output_id", None) if capture is not None else None
538
+ focus = grep_pattern(cmd)
539
+
540
+ # Legacy auto-filter: only on stdout, only when the whole text is at hand
541
+ # (a stream over TEXT_MAX_BYTES has previews only and goes straight to
542
+ # deterministic shaping), more than _FILTER_THRESHOLD_LINES newlines,
543
+ # never a git diagnostic — exactly the trigger 2.111.0 had, so the token
544
+ # profile of a filtered test or build run does not move in S1; what
545
+ # moves is that the dropped text is now recoverable. S2 replaces this.
546
+ stdout_text = out["text"]
467
547
  filtered_note = ""
468
- if (filter_output and raw_stdout.count("\n") > _FILTER_THRESHOLD_LINES
548
+ if (filter_output and stdout_text is not None
549
+ and stdout_text.count("\n") > _FILTER_THRESHOLD_LINES
469
550
  and not _GIT_DIAGNOSTIC.search(cmd)):
470
551
  try:
471
- stdout = handle_filter(
472
- "", raw_stdout, "", 50, "smart", True,
552
+ stdout_text = handle_filter(
553
+ "", stdout_text, "", 50, "smart", True,
473
554
  svc, lambda *a, **kw: a[2],
474
555
  )
475
556
  filtered_note = " [stdout filtered]"
476
557
  stats["filtered"] = True
477
558
  except Exception:
478
- stdout = raw_stdout
559
+ stdout_text = out["text"]
560
+
561
+ shaped_out, info_out = shape_stream(
562
+ full_text=stdout_text, head=out["head"], tail=out["tail"],
563
+ total_bytes=out["bytes"], total_lines=out["lines"], alloc=out_alloc,
564
+ output_id=output_id, focus=focus,
565
+ number_lines=not stats["filtered"]) # filtered text no longer maps to raw line numbers
566
+ if err["bytes"] > 0 or (err["text"] or "").strip():
567
+ shaped_err, info_err = shape_stream(
568
+ full_text=err["text"], head=err["head"], tail=err["tail"],
569
+ total_bytes=err["bytes"], total_lines=err["lines"], alloc=err_alloc,
570
+ output_id=output_id, focus=focus)
571
+ else:
572
+ shaped_err, info_err = "", {"cut": False, "omitted_lines": 0, "omitted_bytes": 0,
573
+ "clipped_lines": 0, "rendered_bytes": 0}
574
+
575
+ cut = bool(info_out["cut"] or info_err["cut"])
576
+ stats["needs_spill"] = bool(stats["filtered"] or cut)
577
+ stats["spilled"] = bool(stats["needs_spill"] and output_id)
578
+ stats["output_id"] = output_id if stats["spilled"] else None
579
+ stats["omitted_lines"] = info_out["omitted_lines"] + info_err["omitted_lines"]
580
+ stats["clipped_lines"] = info_out["clipped_lines"] + info_err["clipped_lines"]
479
581
 
480
582
  if result.get("timed_out"):
481
583
  status = "TIMEOUT"
@@ -484,14 +586,20 @@ def render_shell_response(cmd: str, result: dict, svc, *,
484
586
  else:
485
587
  status = f"FAIL({result.get('exit_code')})"
486
588
 
589
+ size_note = ""
590
+ if cut or stats["spilled"]:
591
+ size_note = (f" (stdout {human_bytes(out['bytes'])}/{out['lines']} lines,"
592
+ f" stderr {human_bytes(err['bytes'])}/{err['lines']} lines,")
593
+ size_note += f" output_id={output_id})" if stats["spilled"] else " not spilled)"
594
+
487
595
  body = (
488
596
  f"{warn}{capped_note}"
489
- f"[c3_shell:{status}] {result.get('duration_ms', 0)}ms{filtered_note}\n"
490
- f"$ {cmd}\n"
491
- f"--- stdout ---\n{stdout.rstrip()}\n"
597
+ f"[c3_shell:{status}] {result.get('duration_ms', 0)}ms{filtered_note}{size_note}\n"
598
+ f"$ {cmd_display(cmd)}\n"
599
+ f"--- stdout ---\n{shaped_out.rstrip()}\n"
492
600
  )
493
- if raw_stderr.strip():
494
- body += f"--- stderr ---\n{raw_stderr.rstrip()}\n"
601
+ if shaped_err.strip():
602
+ body += f"--- stderr ---\n{shaped_err.rstrip()}\n"
495
603
  dependency_hint = _dependency_hint(cmd, result)
496
604
  if dependency_hint:
497
605
  body += f"--- hint ---\n{dependency_hint}\n"
@@ -724,9 +832,102 @@ def _shell_warn_grant(svc, work_cwd: str) -> str | None:
724
832
  return None
725
833
 
726
834
 
835
+ _OUTPUT_ACTIONS = ("read", "search", "tail", "delete")
836
+
837
+
838
+ def _handle_output(output_id: str, action: str, pattern: str, lines, stream: str,
839
+ max_bytes, svc, finalize) -> str:
840
+ """Page a spilled output back by id (2.112.0).
841
+
842
+ The id is resolved for THIS project and THIS host session under the
843
+ CURRENT Access Guard rules — services/shell_output.py documents why a
844
+ spill from a more privileged call must not be readable by a later, less
845
+ privileged one. Every read is re-budgeted like a live response.
846
+ """
847
+ action = (action or "read").strip().lower()
848
+ if action not in _OUTPUT_ACTIONS:
849
+ return (f"[c3_shell:error] output_action must be one of {', '.join(_OUTPUT_ACTIONS)}; "
850
+ f"got {action!r}")
851
+ stream = (stream or "stdout").strip().lower()
852
+ if stream not in ("stdout", "stderr"):
853
+ return "[c3_shell:error] stream must be 'stdout' or 'stderr'"
854
+ project_path = str(svc.project_path)
855
+
856
+ def guard_check(path: str):
857
+ try:
858
+ return access_guard.check(path, "read", project_path)
859
+ except Exception as exc: # evaluator error fails closed
860
+ return access_guard.Denial("<evaluator-error>", "deny", "builtin",
861
+ f"evaluator error: {type(exc).__name__}")
862
+
863
+ store = ShellOutputStore()
864
+ try:
865
+ meta = store.resolve(output_id, project_path=project_path,
866
+ session_id=_grants.session_id(svc), guard_check=guard_check)
867
+ except OutputAccessError as exc:
868
+ return f"[c3_shell:error] {exc}"
869
+ config_default = None
870
+ try:
871
+ config_default = (getattr(svc, "hybrid_config", None) or {}).get("shell_budget_bytes")
872
+ except Exception:
873
+ pass
874
+ budget = effective_budget(max_bytes, config_default=config_default) - 512
875
+ facts = getattr(meta, stream) or {}
876
+ header = (f"[c3_shell:output] {meta.id} {action} {stream} · $ {meta.cmd_display} · "
877
+ f"exit {meta.exit_code}{' (timed out)' if meta.timed_out else ''} · "
878
+ f"{human_bytes(facts.get('bytes', 0))}/{facts.get('lines', 0)} lines · "
879
+ f"captured {meta.created_at[:19]}\n")
880
+ try:
881
+ if action == "delete":
882
+ store.delete(meta)
883
+ body = header + "deleted\n"
884
+ elif action == "search":
885
+ if not pattern:
886
+ return "[c3_shell:error] output_action='search' needs a pattern"
887
+ body = header + store.search(meta, pattern, stream, max_bytes=budget)
888
+ elif action == "tail":
889
+ n = 50
890
+ if isinstance(lines, int) and lines > 0:
891
+ n = lines
892
+ body = header + store.tail(meta, stream, lines=n, max_bytes=budget)
893
+ else:
894
+ window = None
895
+ if isinstance(lines, (list, tuple)) and len(lines) == 2:
896
+ window = (int(lines[0]), int(lines[1]))
897
+ elif isinstance(lines, int) and lines > 0:
898
+ window = (lines, lines)
899
+ elif isinstance(lines, str) and lines.strip():
900
+ parts = [p for p in re.split(r"[,\-:\s]+", lines.strip()) if p]
901
+ if len(parts) == 1:
902
+ window = (int(parts[0]), int(parts[0]))
903
+ elif len(parts) >= 2:
904
+ window = (int(parts[0]), int(parts[1]))
905
+ body = header + store.read(meta, stream, lines=window, max_bytes=budget)
906
+ except (ValueError, re.error) as exc:
907
+ return f"[c3_shell:error] {exc}"
908
+ if not body.endswith("\n"):
909
+ body += "\n"
910
+ return finalize_with_tokens(
911
+ finalize, svc, "c3_shell",
912
+ {"output_id": output_id, "output_action": action},
913
+ body, f"shell output {action} {output_id}",
914
+ detail={"cmd_class": "output", "output_id": output_id, "output_action": action,
915
+ "response_bytes": len(body.encode("utf-8", errors="replace")),
916
+ "response_tokens": count_tokens(body)},
917
+ response_tokens=count_tokens(body),
918
+ )
919
+
920
+
727
921
  async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
728
922
  log: bool, svc, finalize, env_creds: str = "",
729
- enable_creds: bool = True) -> str:
923
+ enable_creds: bool = True, *, output_id: str = "",
924
+ output_action: str = "", pattern: str = "",
925
+ lines=None, stream: str = "stdout",
926
+ max_bytes: int | None = None) -> str:
927
+ if output_id:
928
+ return await asyncio.to_thread(
929
+ _handle_output, output_id, output_action, pattern, lines, stream,
930
+ max_bytes, svc, finalize)
730
931
  if not cmd or not cmd.strip():
731
932
  return "[c3_shell:error] empty command"
732
933
  # _BLOCKED never consults grants — no approval flow reaches the
@@ -860,16 +1061,23 @@ async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
860
1061
  else None
861
1062
  )
862
1063
 
1064
+ # Decoded values a child process may echo (env dumps, set, crash output)
1065
+ # are scrubbed IN THE STREAM by _run_sync's redactor, before a byte
1066
+ # reaches the spool or any preview, so neither the response nor a spill
1067
+ # ever holds one.
863
1068
  result = await asyncio.to_thread(
864
1069
  _run_sync, exec_cmd, work_cwd, timeout, extra_env or None)
1070
+ capture = result.get("capture")
865
1071
 
866
1072
  swept_ghosts = _sweep_new_ghost_files(ghost_root, _ghosts_before)
867
1073
 
868
- # Scrub decoded values a child process may have echoed (env dumps, set,
869
- # crash output) BEFORE filtering/ledger/logging see the text.
1074
+ # Belt over the in-stream scrub for the small-output text (and for a
1075
+ # result a test handed in without a capture).
870
1076
  if enable_creds:
871
- result["stdout"] = _creds.redact_text(result["stdout"])
872
- result["stderr"] = _creds.redact_text(result["stderr"])
1077
+ if result.get("stdout") is not None:
1078
+ result["stdout"] = _creds.redact_text(result["stdout"])
1079
+ if result.get("stderr") is not None:
1080
+ result["stderr"] = _creds.redact_text(result["stderr"])
873
1081
  if cred_names:
874
1082
  # Usage state is keyed by entry NAME; collapse dotted field refs.
875
1083
  _creds.touch_last_used(
@@ -936,12 +1144,45 @@ async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
936
1144
  f"run_in_background, which is not bound by this limit.\n"
937
1145
  )
938
1146
 
939
- body, stats = await asyncio.to_thread(
940
- render_shell_response, cmd, result, svc,
941
- filter_output=filter_output, warn=warn, capped_note=capped_note,
942
- touched_files=touched_files, cred_names=cred_names,
943
- swept_ghosts=swept_ghosts,
944
- )
1147
+ try:
1148
+ body, stats = await asyncio.to_thread(
1149
+ render_shell_response, cmd, result, svc,
1150
+ filter_output=filter_output, warn=warn, capped_note=capped_note,
1151
+ touched_files=touched_files, cred_names=cred_names,
1152
+ swept_ghosts=swept_ghosts, max_bytes=max_bytes,
1153
+ )
1154
+ # Keep the raw streams only when the body dropped something: the
1155
+ # spill is what makes a clipped or filtered response recoverable
1156
+ # without re-running the command (docs/shell-output.md).
1157
+ if capture is not None:
1158
+ if stats.get("needs_spill"):
1159
+ try:
1160
+ ShellOutputStore().promote(
1161
+ capture, project_path=str(svc.project_path),
1162
+ session_id=_grants.session_id(svc), cmd=cmd, cwd=work_cwd,
1163
+ guard_paths=_scan_candidates(cmd, work_cwd),
1164
+ exit_code=result.get("exit_code", -1),
1165
+ timed_out=bool(result.get("timed_out")),
1166
+ duration_ms=result.get("duration_ms", 0))
1167
+ except Exception as exc:
1168
+ stats["spilled"] = False
1169
+ stats["output_id"] = None
1170
+ body += (f"[c3_shell:note] the raw output could not be kept "
1171
+ f"({type(exc).__name__}: {exc}); the output id above is not retrievable\n")
1172
+ try:
1173
+ capture.discard()
1174
+ except Exception:
1175
+ pass
1176
+ else:
1177
+ capture.discard()
1178
+ except Exception:
1179
+ if capture is not None:
1180
+ try:
1181
+ capture.discard()
1182
+ except Exception:
1183
+ pass
1184
+ raise
1185
+ stats.pop("needs_spill", None)
945
1186
 
946
1187
  if result["timed_out"]:
947
1188
  status = "TIMEOUT"