code-context-control 2.153.0__tar.gz → 2.154.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.
- {code_context_control-2.153.0 → code_context_control-2.154.0}/PKG-INFO +1 -1
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/c3.py +63 -1
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/parser.py +11 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/index.html +4 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/PKG-INFO +1 -1
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/SOURCES.txt +2 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/pyproject.toml +1 -1
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/embedding_index.py +434 -4
- code_context_control-2.154.0/tests/test_embedding_store_safety.py +228 -0
- code_context_control-2.154.0/tests/test_quarantine_sweep.py +178 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/LICENSE +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/README.md +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/_shell_writes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/commands/common.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/docs.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/edits.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/access.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/ci.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/delegate.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/jira.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/sessions.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_agent_model.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_codex_lifecycle.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_read.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_end.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_open.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_server.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_access.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_sessions.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/hub_tokens.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/mcp_server.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/progress.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/server.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_edit_report.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_grants.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/ci.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/override.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/project.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/read.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/search.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/session.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_job.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_nudge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_parsers.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/shell_render.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/status.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/api.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/app.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/codex_patch.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/config.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/grok_payload.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/hook_command.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/host.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/ide.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/core/web_security.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/config.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/listeners.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/oracle_server.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_poll.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/client_tokens.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/mobile_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/settings.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/access_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/access_telemetry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/activity_log.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_base.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_downshift.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agent_locks.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agents.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/agents_workflow.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/artifact_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/atomic_json.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/auto_memory.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/background_service.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/delegate_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/delegate_write_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/map_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/search_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bench/shell_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/builtin_leases.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_act.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_cache.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_expr.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_failures.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_github.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_impact.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_intel.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_runner.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ci_workflow.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/claude_md.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/codex_integration.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/codex_transcript.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/compressor.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/conversation_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/cred_audit.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/cred_telemetry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/credential_backup.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/credential_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_agents.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_hints.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/delegate_write.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/dir_map.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/doc_index.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/edit_blobs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/enforcement_policy.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/error_reporting.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/file_map.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/file_memory.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/git_context.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/grok_integration.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/grok_transcript.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/host_sessions.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/hub_service.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/index_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/indexer.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_client.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/jira_links.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/lexical_index.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_activation.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/mask_presets.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_graph.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_queue.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/metrics.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/notifications.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_client.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/oracle_service.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/output_filter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_costs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_grants.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_policy.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_requests.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/override_wake.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/parser.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/project_manager.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/project_runtime.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/protocol.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/proxy_state.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/read_stamps.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/repo_map.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/repo_shape.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/reranker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retention.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retrieval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/router.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/runtime.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/scanner.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_catalog.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_live.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_manager.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/session_preloader.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/shell_jobs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/shell_output.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/subprojects.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/task_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/telemetry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/terminal_launch.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/text_index.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/time_tracker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/transcript_index.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/validation_cache.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/vector_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/watcher.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/setup.cfg +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_activity_log_find_last.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_atomic_json.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_background_service.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_builtin_leases.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_builtin_modes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_act.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_cache.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_dag_ownership.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_expr.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_impact.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_intel.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_claude_md_updater.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_codex_mcp_transport.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_codex_native.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_confirm_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_confirm_guard_wiring.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cooccurrence_bounds.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cred_audit.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_cred_telemetry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_backup.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_login_kind.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credential_ui_parity.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_adoption_d4.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_auto.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_claude_d1.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_grok.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_host_d2.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_scout_d3.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_telemetry_d0.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_thinking_cap.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_tuning.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_write.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_delegate_write_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_dir_map_c3.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_eol_atomic.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_outcome.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_report.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_revert.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_stale.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_embedding_index_corrupt_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_env_parser.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_file_map_c1.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_grok_native.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_codex_compat.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_grok_compat.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_session_end.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_access_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_builtin_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_hook_migration.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_ide_cmd.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_oracle_service_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_override_policy_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_override_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_rules_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_sessions_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_index_cap_not_hardcoded.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_init_clear_preserves_user_content.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_large_files_c4.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_map_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_map_telemetry_c0.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_clients.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_extras.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_jobs_ci.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_override_costs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_override_routes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_notification_kinds.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_health_probe.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_loopback.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_costs.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_grants.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_requests.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_override_wake.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_read_outside_root.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_repo_shape.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_retention.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p1.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p2.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p2b.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p3.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p4.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_search_p5.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_catalog.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_live.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_session_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_budget_s1.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_eval.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_jobs_s3.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_nudge_s4.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_output.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_parsers_s2.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_telemetry_s0.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_write_ledger.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_shell_writes.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_clear_scope.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_depth_consumers.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subproject_linkage_api.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_token_tracking.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_ui_jsx_syntax.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_validate.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_watcher_pruning.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/backend.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/main.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.153.0 → code_context_control-2.154.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.
|
|
3
|
+
Version: 2.154.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.
|
|
94
|
+
__version__ = "2.154.0"
|
|
95
95
|
|
|
96
96
|
# The PreToolUse matcher for native subagent calls (installer and hub migration).
|
|
97
97
|
AGENT_MATCHER = "Agent|Task"
|
|
@@ -1288,6 +1288,14 @@ def cmd_init(args):
|
|
|
1288
1288
|
except Exception:
|
|
1289
1289
|
pass
|
|
1290
1290
|
|
|
1291
|
+
swept = _prune_project_quarantines(project_path)
|
|
1292
|
+
if swept["pruned"]:
|
|
1293
|
+
print(f" Fixed : freed {_format_bytes(swept['bytes'])} from "
|
|
1294
|
+
f"{len(swept['pruned'])} old embedding quarantine dir(s)")
|
|
1295
|
+
for name in swept["failed"]:
|
|
1296
|
+
print(f" [warn] {name} could not be fully removed (in use?); "
|
|
1297
|
+
"run 'c3 prune-quarantine' later")
|
|
1298
|
+
|
|
1291
1299
|
# Delegate backends
|
|
1292
1300
|
try:
|
|
1293
1301
|
from services.ollama_client import OllamaClient as _OC
|
|
@@ -1463,6 +1471,59 @@ def cmd_index(args):
|
|
|
1463
1471
|
return common_cmd_index(args, _command_deps())
|
|
1464
1472
|
|
|
1465
1473
|
|
|
1474
|
+
def _format_bytes(n: float) -> str:
|
|
1475
|
+
for unit in ("B", "KB", "MB", "GB"):
|
|
1476
|
+
if n < 1024:
|
|
1477
|
+
return f"{n:.0f} {unit}" if unit == "B" else f"{n:.1f} {unit}"
|
|
1478
|
+
n /= 1024
|
|
1479
|
+
return f"{n:.1f} TB"
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
def _prune_project_quarantines(project_path: str, keep=None, dry_run: bool = False) -> dict:
|
|
1483
|
+
"""Prune one project's ``.c3/embeddings/quarantine_corrupt_*`` dirs; returns the sweep result."""
|
|
1484
|
+
from cli.progress import ProgressLine
|
|
1485
|
+
from services.embedding_index import prune_quarantines
|
|
1486
|
+
|
|
1487
|
+
prog = ProgressLine()
|
|
1488
|
+
try:
|
|
1489
|
+
return prune_quarantines(
|
|
1490
|
+
Path(project_path) / CONFIG_DIR / "embeddings", keep=keep, dry_run=dry_run,
|
|
1491
|
+
on_progress=lambda done, total: prog.update(
|
|
1492
|
+
f" removing quarantine copies {done}/{total}"))
|
|
1493
|
+
finally:
|
|
1494
|
+
prog.done()
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
def cmd_prune_quarantine(args):
|
|
1498
|
+
"""Delete old corrupt-embedding-store quarantine copies, keeping the newest few."""
|
|
1499
|
+
if args.all:
|
|
1500
|
+
from services.project_manager import ProjectManager
|
|
1501
|
+
projects = [p["path"] for p in ProjectManager().list_registered()]
|
|
1502
|
+
else:
|
|
1503
|
+
projects = [str(Path(args.project_path or ".").resolve())]
|
|
1504
|
+
|
|
1505
|
+
verb = "would remove" if args.dry_run else "removed"
|
|
1506
|
+
pruned = failed = freed = 0
|
|
1507
|
+
for project in projects:
|
|
1508
|
+
res = _prune_project_quarantines(project, keep=args.keep, dry_run=args.dry_run)
|
|
1509
|
+
if not (res["pruned"] or res["failed"]):
|
|
1510
|
+
continue
|
|
1511
|
+
pruned += len(res["pruned"])
|
|
1512
|
+
failed += len(res["failed"])
|
|
1513
|
+
freed += res["bytes"]
|
|
1514
|
+
print(f"{project}: {verb} {len(res['pruned'])} quarantine dir(s), "
|
|
1515
|
+
f"{_format_bytes(res['bytes'])}; kept {len(res['kept'])}")
|
|
1516
|
+
for name in res["failed"]:
|
|
1517
|
+
print(f" [warn] {name} could not be fully removed (in use?); run again later")
|
|
1518
|
+
|
|
1519
|
+
if not (pruned or failed):
|
|
1520
|
+
print(f"Nothing to prune in {len(projects)} project(s).")
|
|
1521
|
+
return
|
|
1522
|
+
print(f"\n{verb.capitalize()} {pruned} quarantine dir(s), {_format_bytes(freed)}.")
|
|
1523
|
+
if failed:
|
|
1524
|
+
sys.exit(1)
|
|
1525
|
+
|
|
1526
|
+
|
|
1466
1527
|
def cmd_compress(args):
|
|
1467
1528
|
"""Compress a file and show results."""
|
|
1468
1529
|
return common_cmd_compress(args, _command_deps())
|
|
@@ -9603,6 +9664,7 @@ def main():
|
|
|
9603
9664
|
"enforce": cmd_enforce,
|
|
9604
9665
|
"override": cmd_override,
|
|
9605
9666
|
"locks": cmd_locks,
|
|
9667
|
+
"prune-quarantine": cmd_prune_quarantine,
|
|
9606
9668
|
"ci": cmd_ci,
|
|
9607
9669
|
"oracle": cmd_oracle,
|
|
9608
9670
|
"upgrade": cmd_upgrade,
|
|
@@ -37,6 +37,17 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
37
37
|
p_upgrade.add_argument("--check", action="store_true",
|
|
38
38
|
help="Only report whether a newer version exists; don't install")
|
|
39
39
|
|
|
40
|
+
p_prune = subparsers.add_parser(
|
|
41
|
+
"prune-quarantine",
|
|
42
|
+
help="Delete old corrupt-embedding-store quarantine copies (frees disk)")
|
|
43
|
+
p_prune.add_argument("project_path", nargs="?", default=".")
|
|
44
|
+
p_prune.add_argument("--all", action="store_true",
|
|
45
|
+
help="Every project registered with the hub, not just this one")
|
|
46
|
+
p_prune.add_argument("--keep", type=int, default=None,
|
|
47
|
+
help="Newest quarantine dirs to keep per project (default 2)")
|
|
48
|
+
p_prune.add_argument("--dry-run", action="store_true",
|
|
49
|
+
help="Report what would be removed without deleting")
|
|
50
|
+
|
|
40
51
|
p_index = subparsers.add_parser("index", help="Rebuild code index")
|
|
41
52
|
p_index.add_argument("--max-files", type=int, default=None,
|
|
42
53
|
help="Cap files indexed this run. Unset reads "
|
|
@@ -663,6 +663,10 @@
|
|
|
663
663
|
<code>c3 map {status|ensure|refresh}</code>
|
|
664
664
|
<span>Inspect or rebuild the live repo map at .c3/MAP.md</span>
|
|
665
665
|
</div>
|
|
666
|
+
<div class="quick-item">
|
|
667
|
+
<code>c3 prune-quarantine [--all] [--dry-run]</code>
|
|
668
|
+
<span>Free disk held by old corrupt-embedding-store copies in .c3/embeddings (<code>c3 init</code> runs it too)</span>
|
|
669
|
+
</div>
|
|
666
670
|
<div class="quick-item">
|
|
667
671
|
<code>c3 hub</code>
|
|
668
672
|
<span>Launch the Project Hub web dashboard (localhost:3330)</span>
|
{code_context_control-2.153.0 → code_context_control-2.154.0}/code_context_control.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.154.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
|
|
@@ -407,6 +407,7 @@ tests/test_edit_stale.py
|
|
|
407
407
|
tests/test_edit_verify.py
|
|
408
408
|
tests/test_embedding_index_corrupt_store.py
|
|
409
409
|
tests/test_embedding_index_deadlock.py
|
|
410
|
+
tests/test_embedding_store_safety.py
|
|
410
411
|
tests/test_enforcement_flip.py
|
|
411
412
|
tests/test_enforcement_policy.py
|
|
412
413
|
tests/test_enforcement_routes.py
|
|
@@ -511,6 +512,7 @@ tests/test_pm_api.py
|
|
|
511
512
|
tests/test_project_manager.py
|
|
512
513
|
tests/test_project_manager_merge.py
|
|
513
514
|
tests/test_project_tool.py
|
|
515
|
+
tests/test_quarantine_sweep.py
|
|
514
516
|
tests/test_read_coercion.py
|
|
515
517
|
tests/test_read_edit_parity.py
|
|
516
518
|
tests/test_read_outside_root.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code-context-control"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.154.0"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -10,8 +10,12 @@ import gc
|
|
|
10
10
|
import hashlib
|
|
11
11
|
import json
|
|
12
12
|
import logging
|
|
13
|
+
import os
|
|
13
14
|
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
14
17
|
import threading
|
|
18
|
+
import time
|
|
15
19
|
from datetime import datetime
|
|
16
20
|
from pathlib import Path
|
|
17
21
|
|
|
@@ -28,6 +32,336 @@ DEFAULT_MIN_SCORE_OTHER = 0.55
|
|
|
28
32
|
# and degrade rather than block. See _acquire_build_lock().
|
|
29
33
|
_BUILD_LOCK_WAIT_SECONDS = 30.0
|
|
30
34
|
|
|
35
|
+
# ── Store safety (issue #180) ─────────────────────────────
|
|
36
|
+
# A damaged HNSW segment does not just fail to load. chromadb's loader
|
|
37
|
+
# allocates as it reads, so one bad segment made a single MCP server commit
|
|
38
|
+
# 140-200 GB before the load gave up, and every new session repeated it
|
|
39
|
+
# (measured 2026-10-02: count() on a copy of the store, 165 GB private, 0.25 GB
|
|
40
|
+
# working set, then "Error loading hnsw index"). An in-process probe cannot
|
|
41
|
+
# undo that commit, so an existing store is first opened in a child process
|
|
42
|
+
# under a memory cap, and only opened here if the child could read it.
|
|
43
|
+
_PROBE_TIMEOUT_SECONDS = 180.0
|
|
44
|
+
_PROBE_POLL_SECONDS = 0.1
|
|
45
|
+
_PROBE_MIN_CAP_BYTES = 4 * 1024 ** 3 # floor; scaled up for big stores below
|
|
46
|
+
_PROBE_CAP_STORE_MULTIPLE = 4
|
|
47
|
+
_PROBE_OK_FILE = "store_probe_ok.json"
|
|
48
|
+
_PROBE_OK_MARKER = "C3_STORE_PROBE_OK"
|
|
49
|
+
# Each quarantine keeps a full copy of the store for post-mortem. Unbounded,
|
|
50
|
+
# that reached 284 copies and 384 GB in one project; keep only the newest few.
|
|
51
|
+
_QUARANTINE_KEEP = 2
|
|
52
|
+
_OWNER_LOCK_FILE = "chromadb.owner.lock"
|
|
53
|
+
|
|
54
|
+
_PROBE_CHILD_CODE = """
|
|
55
|
+
import sys
|
|
56
|
+
import chromadb
|
|
57
|
+
from chromadb.config import Settings
|
|
58
|
+
client = chromadb.PersistentClient(
|
|
59
|
+
path=sys.argv[1], settings=Settings(anonymized_telemetry=False))
|
|
60
|
+
listed = client.list_collections()
|
|
61
|
+
names = {c if isinstance(c, str) else getattr(c, "name", "") for c in listed}
|
|
62
|
+
if sys.argv[2] in names:
|
|
63
|
+
client.get_collection(sys.argv[2]).count()
|
|
64
|
+
print("C3_STORE_PROBE_OK", flush=True)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class StoreBusyError(RuntimeError):
|
|
69
|
+
"""Another C3 process owns this project's embedding store."""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _tree_bytes(path: Path) -> int:
|
|
73
|
+
total = 0
|
|
74
|
+
for root, _dirs, files in os.walk(path):
|
|
75
|
+
for name in files:
|
|
76
|
+
try:
|
|
77
|
+
total += os.lstat(os.path.join(root, name)).st_size
|
|
78
|
+
except OSError:
|
|
79
|
+
pass
|
|
80
|
+
return total
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def prune_quarantines(index_dir: Path, keep: int | None = None,
|
|
84
|
+
dry_run: bool = False, on_progress=None) -> dict:
|
|
85
|
+
"""Delete all but the newest *keep* ``quarantine_corrupt_*`` dirs in *index_dir*.
|
|
86
|
+
|
|
87
|
+
``keep=None`` keeps the default of two.
|
|
88
|
+
|
|
89
|
+
Newest is by name, which embeds the quarantine timestamp. Symlinks and
|
|
90
|
+
non-directories matching the pattern are never touched. A dir that cannot
|
|
91
|
+
be fully removed is reported in ``failed`` and retried by the next call.
|
|
92
|
+
``on_progress(done, total)`` runs after each dir. With ``dry_run`` nothing
|
|
93
|
+
is deleted and ``pruned`` / ``bytes`` describe what a real run would remove.
|
|
94
|
+
|
|
95
|
+
Returns ``{"kept": [...], "pruned": [...], "failed": [...], "bytes": int}``
|
|
96
|
+
with directory names; ``bytes`` is the size freed (or that would be freed).
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
found = sorted(p for p in Path(index_dir).glob("quarantine_corrupt_*")
|
|
100
|
+
if p.is_dir() and not p.is_symlink())
|
|
101
|
+
except OSError:
|
|
102
|
+
found = []
|
|
103
|
+
keep = _QUARANTINE_KEEP if keep is None else max(int(keep), 0)
|
|
104
|
+
doomed = found[:len(found) - keep] if keep < len(found) else []
|
|
105
|
+
result = {"kept": [p.name for p in found[len(doomed):]],
|
|
106
|
+
"pruned": [], "failed": [], "bytes": 0}
|
|
107
|
+
for done, old in enumerate(doomed, 1):
|
|
108
|
+
size = _tree_bytes(old)
|
|
109
|
+
if dry_run:
|
|
110
|
+
result["pruned"].append(old.name)
|
|
111
|
+
result["bytes"] += size
|
|
112
|
+
else:
|
|
113
|
+
shutil.rmtree(old, ignore_errors=True)
|
|
114
|
+
left = _tree_bytes(old) if old.exists() else 0
|
|
115
|
+
result["bytes"] += size - left
|
|
116
|
+
result["failed" if old.exists() else "pruned"].append(old.name)
|
|
117
|
+
if on_progress:
|
|
118
|
+
on_progress(done, len(doomed))
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _private_bytes(proc) -> int:
|
|
123
|
+
"""Committed (Windows) or resident (POSIX) bytes of a psutil.Process."""
|
|
124
|
+
info = proc.memory_info()
|
|
125
|
+
return int(getattr(info, "private", 0) or info.rss)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _windows_job_memory_cap(pid: int, cap_bytes: int):
|
|
129
|
+
"""Put *pid* in a Job Object whose per-process commit limit is *cap_bytes*.
|
|
130
|
+
|
|
131
|
+
Polling alone is not enough here: the corrupt-segment loader committed
|
|
132
|
+
~100 GB between two 100 ms polls (measured 2026-10-02). Under a job limit
|
|
133
|
+
the allocation fails inside the child instead. Returns the job handle
|
|
134
|
+
(keep it open while the child runs) or None when unsupported.
|
|
135
|
+
"""
|
|
136
|
+
if os.name != "nt":
|
|
137
|
+
return None
|
|
138
|
+
try:
|
|
139
|
+
import ctypes
|
|
140
|
+
from ctypes import wintypes
|
|
141
|
+
|
|
142
|
+
k32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
143
|
+
|
|
144
|
+
class IO_COUNTERS(ctypes.Structure):
|
|
145
|
+
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
|
146
|
+
"ReadOperationCount", "WriteOperationCount", "OtherOperationCount",
|
|
147
|
+
"ReadTransferCount", "WriteTransferCount", "OtherTransferCount")]
|
|
148
|
+
|
|
149
|
+
class BASIC(ctypes.Structure):
|
|
150
|
+
_fields_ = [
|
|
151
|
+
("PerProcessUserTimeLimit", ctypes.c_longlong),
|
|
152
|
+
("PerJobUserTimeLimit", ctypes.c_longlong),
|
|
153
|
+
("LimitFlags", wintypes.DWORD),
|
|
154
|
+
("MinimumWorkingSetSize", ctypes.c_size_t),
|
|
155
|
+
("MaximumWorkingSetSize", ctypes.c_size_t),
|
|
156
|
+
("ActiveProcessLimit", wintypes.DWORD),
|
|
157
|
+
("Affinity", ctypes.c_size_t),
|
|
158
|
+
("PriorityClass", wintypes.DWORD),
|
|
159
|
+
("SchedulingClass", wintypes.DWORD),
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
class EXTENDED(ctypes.Structure):
|
|
163
|
+
_fields_ = [
|
|
164
|
+
("BasicLimitInformation", BASIC),
|
|
165
|
+
("IoInfo", IO_COUNTERS),
|
|
166
|
+
("ProcessMemoryLimit", ctypes.c_size_t),
|
|
167
|
+
("JobMemoryLimit", ctypes.c_size_t),
|
|
168
|
+
("PeakProcessMemoryUsed", ctypes.c_size_t),
|
|
169
|
+
("PeakJobMemoryUsed", ctypes.c_size_t),
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x100
|
|
173
|
+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
|
|
174
|
+
JobObjectExtendedLimitInformation = 9
|
|
175
|
+
PROCESS_SET_QUOTA, PROCESS_TERMINATE = 0x0100, 0x0001
|
|
176
|
+
|
|
177
|
+
k32.CreateJobObjectW.restype = wintypes.HANDLE
|
|
178
|
+
k32.OpenProcess.restype = wintypes.HANDLE
|
|
179
|
+
job = k32.CreateJobObjectW(None, None)
|
|
180
|
+
if not job:
|
|
181
|
+
return None
|
|
182
|
+
info = EXTENDED()
|
|
183
|
+
info.BasicLimitInformation.LimitFlags = (
|
|
184
|
+
JOB_OBJECT_LIMIT_PROCESS_MEMORY | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)
|
|
185
|
+
info.ProcessMemoryLimit = cap_bytes
|
|
186
|
+
if not k32.SetInformationJobObject(
|
|
187
|
+
wintypes.HANDLE(job), JobObjectExtendedLimitInformation,
|
|
188
|
+
ctypes.byref(info), ctypes.sizeof(info)):
|
|
189
|
+
k32.CloseHandle(wintypes.HANDLE(job))
|
|
190
|
+
return None
|
|
191
|
+
proc = k32.OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, False, pid)
|
|
192
|
+
if not proc:
|
|
193
|
+
k32.CloseHandle(wintypes.HANDLE(job))
|
|
194
|
+
return None
|
|
195
|
+
try:
|
|
196
|
+
assigned = k32.AssignProcessToJobObject(wintypes.HANDLE(job), wintypes.HANDLE(proc))
|
|
197
|
+
finally:
|
|
198
|
+
k32.CloseHandle(wintypes.HANDLE(proc))
|
|
199
|
+
if not assigned:
|
|
200
|
+
k32.CloseHandle(wintypes.HANDLE(job))
|
|
201
|
+
return None
|
|
202
|
+
return job
|
|
203
|
+
except Exception:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _close_job(job) -> None:
|
|
208
|
+
if job is None:
|
|
209
|
+
return
|
|
210
|
+
try:
|
|
211
|
+
import ctypes
|
|
212
|
+
from ctypes import wintypes
|
|
213
|
+
ctypes.WinDLL("kernel32").CloseHandle(wintypes.HANDLE(job))
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def run_isolated_probe(
|
|
219
|
+
args: list,
|
|
220
|
+
*,
|
|
221
|
+
cap_bytes: int,
|
|
222
|
+
timeout: float = _PROBE_TIMEOUT_SECONDS,
|
|
223
|
+
marker: str = _PROBE_OK_MARKER,
|
|
224
|
+
) -> tuple:
|
|
225
|
+
"""Run ``python <args>`` in a child that is killed if it outgrows *cap_bytes*.
|
|
226
|
+
|
|
227
|
+
Returns ``(ok, reason)``. ``ok`` needs a zero exit AND *marker* on stdout,
|
|
228
|
+
so a child that dies quietly is a failure, not a pass. The cap is enforced
|
|
229
|
+
by polling the child's memory (psutil when installed; RLIMIT_AS on POSIX
|
|
230
|
+
otherwise), which bounds the damage to the cap plus one poll interval.
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
import psutil # optional: C3 does not declare it
|
|
234
|
+
except Exception:
|
|
235
|
+
psutil = None
|
|
236
|
+
|
|
237
|
+
preexec = None
|
|
238
|
+
if psutil is None and os.name != "nt":
|
|
239
|
+
def preexec(): # pragma: no cover - POSIX without psutil
|
|
240
|
+
try: # best effort: macOS refuses to lower RLIMIT_AS
|
|
241
|
+
import resource
|
|
242
|
+
resource.setrlimit(resource.RLIMIT_AS, (cap_bytes, cap_bytes))
|
|
243
|
+
except (ValueError, OSError):
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
|
|
247
|
+
import tempfile
|
|
248
|
+
|
|
249
|
+
# Files, not pipes: a child that logs more than a pipe buffer while nobody
|
|
250
|
+
# reads it would block and be misreported as a timeout.
|
|
251
|
+
with tempfile.TemporaryFile() as out_f, tempfile.TemporaryFile() as err_f:
|
|
252
|
+
child = subprocess.Popen(
|
|
253
|
+
[sys.executable, *args],
|
|
254
|
+
stdin=subprocess.DEVNULL,
|
|
255
|
+
stdout=out_f,
|
|
256
|
+
stderr=err_f,
|
|
257
|
+
preexec_fn=preexec,
|
|
258
|
+
creationflags=creationflags,
|
|
259
|
+
)
|
|
260
|
+
job = _windows_job_memory_cap(child.pid, cap_bytes)
|
|
261
|
+
try:
|
|
262
|
+
reason = _watch_probe(child, psutil, cap_bytes, timeout)
|
|
263
|
+
finally:
|
|
264
|
+
_close_job(job)
|
|
265
|
+
if reason:
|
|
266
|
+
return False, reason
|
|
267
|
+
out_f.seek(0)
|
|
268
|
+
err_f.seek(0)
|
|
269
|
+
text = out_f.read().decode("utf-8", "replace")
|
|
270
|
+
err = err_f.read().decode("utf-8", "replace")
|
|
271
|
+
if child.returncode == 0 and marker in text:
|
|
272
|
+
return True, ""
|
|
273
|
+
tail = err.strip().splitlines()
|
|
274
|
+
detail = tail[-1] if tail else f"exit code {child.returncode}"
|
|
275
|
+
capped = " (under a hard memory cap)" if job is not None else ""
|
|
276
|
+
return False, f"probe failed{capped}: {detail[:300]}"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _watch_probe(child, psutil, cap_bytes, timeout) -> str:
|
|
280
|
+
"""Wait for *child*; kill it on the cap or the deadline. Returns a reason or ""."""
|
|
281
|
+
watched = None
|
|
282
|
+
if psutil is not None:
|
|
283
|
+
try:
|
|
284
|
+
watched = psutil.Process(child.pid)
|
|
285
|
+
except Exception:
|
|
286
|
+
watched = None
|
|
287
|
+
deadline = time.monotonic() + timeout
|
|
288
|
+
peak = 0
|
|
289
|
+
reason = ""
|
|
290
|
+
while child.poll() is None:
|
|
291
|
+
if watched is not None:
|
|
292
|
+
try:
|
|
293
|
+
peak = max(peak, _private_bytes(watched))
|
|
294
|
+
except Exception:
|
|
295
|
+
pass
|
|
296
|
+
if peak > cap_bytes:
|
|
297
|
+
reason = (f"probe exceeded its memory cap "
|
|
298
|
+
f"({peak / 1024 ** 3:.1f} GB > {cap_bytes / 1024 ** 3:.1f} GB)")
|
|
299
|
+
break
|
|
300
|
+
if time.monotonic() >= deadline:
|
|
301
|
+
reason = f"probe timed out after {timeout:.0f}s"
|
|
302
|
+
break
|
|
303
|
+
time.sleep(_PROBE_POLL_SECONDS)
|
|
304
|
+
if reason:
|
|
305
|
+
child.kill()
|
|
306
|
+
try:
|
|
307
|
+
child.wait(timeout=30)
|
|
308
|
+
except Exception:
|
|
309
|
+
pass
|
|
310
|
+
return reason
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class _OwnerLock:
|
|
314
|
+
"""Non-blocking, process-lifetime exclusive lock on one file.
|
|
315
|
+
|
|
316
|
+
chromadb's PersistentClient is not safe for several processes at once, yet
|
|
317
|
+
every C3 MCP server for a project opened the same store. The OS drops the
|
|
318
|
+
lock when the holder dies, so a crashed server never strands it.
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
def __init__(self, path: Path):
|
|
322
|
+
self.path = path
|
|
323
|
+
self._fh = None
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def held(self) -> bool:
|
|
327
|
+
return self._fh is not None
|
|
328
|
+
|
|
329
|
+
def try_acquire(self) -> bool:
|
|
330
|
+
if self._fh is not None:
|
|
331
|
+
return True
|
|
332
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
333
|
+
fh = open(self.path, "ab") # never read/written; fd exists to hold the lock
|
|
334
|
+
try:
|
|
335
|
+
if os.name == "nt":
|
|
336
|
+
import msvcrt
|
|
337
|
+
fh.seek(0)
|
|
338
|
+
msvcrt.locking(fh.fileno(), msvcrt.LK_NBLCK, 1)
|
|
339
|
+
else:
|
|
340
|
+
import fcntl
|
|
341
|
+
fcntl.flock(fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
342
|
+
except OSError:
|
|
343
|
+
fh.close()
|
|
344
|
+
return False
|
|
345
|
+
self._fh = fh
|
|
346
|
+
return True
|
|
347
|
+
|
|
348
|
+
def release(self) -> None:
|
|
349
|
+
fh, self._fh = self._fh, None
|
|
350
|
+
if fh is None:
|
|
351
|
+
return
|
|
352
|
+
try:
|
|
353
|
+
if os.name == "nt":
|
|
354
|
+
import msvcrt
|
|
355
|
+
fh.seek(0)
|
|
356
|
+
msvcrt.locking(fh.fileno(), msvcrt.LK_UNLCK, 1)
|
|
357
|
+
else:
|
|
358
|
+
import fcntl
|
|
359
|
+
fcntl.flock(fh.fileno(), fcntl.LOCK_UN)
|
|
360
|
+
except OSError:
|
|
361
|
+
pass
|
|
362
|
+
finally:
|
|
363
|
+
fh.close()
|
|
364
|
+
|
|
31
365
|
|
|
32
366
|
class EmbeddingIndex:
|
|
33
367
|
"""Semantic code search via embeddings over CodeIndex chunks."""
|
|
@@ -62,6 +396,8 @@ class EmbeddingIndex:
|
|
|
62
396
|
self._chroma_client = None
|
|
63
397
|
self._collection = None
|
|
64
398
|
self._available = False
|
|
399
|
+
self._unavailable_why = "" # set when chromadb is installed but unusable
|
|
400
|
+
self._owner_lock = _OwnerLock(self._index_dir / _OWNER_LOCK_FILE)
|
|
65
401
|
self._ollama_ok = False
|
|
66
402
|
self._ollama_up = False
|
|
67
403
|
self._model_ok = False
|
|
@@ -112,9 +448,17 @@ class EmbeddingIndex:
|
|
|
112
448
|
self._open_chroma()
|
|
113
449
|
self._available = True
|
|
114
450
|
self._drop_legacy_collection()
|
|
115
|
-
except
|
|
451
|
+
except StoreBusyError as e:
|
|
452
|
+
log.info("embedding index disabled in this process: %s", e)
|
|
453
|
+
self._unavailable_why = str(e)
|
|
454
|
+
self._available = False
|
|
455
|
+
except ImportError as e:
|
|
116
456
|
log.debug("chromadb unavailable for embedding index: %s", e)
|
|
117
457
|
self._available = False
|
|
458
|
+
except Exception as e:
|
|
459
|
+
log.warning("embedding store unusable: %s", e)
|
|
460
|
+
self._unavailable_why = f"embedding store unusable: {e}"
|
|
461
|
+
self._available = False
|
|
118
462
|
|
|
119
463
|
try:
|
|
120
464
|
self._ollama_up = self.ollama.is_available(timeout=2)
|
|
@@ -144,7 +488,19 @@ class EmbeddingIndex:
|
|
|
144
488
|
from chromadb.config import Settings
|
|
145
489
|
|
|
146
490
|
persist_dir = self._index_dir / "chromadb"
|
|
491
|
+
if not self._owner_lock.try_acquire():
|
|
492
|
+
raise StoreBusyError(
|
|
493
|
+
"embedding store is in use by another C3 server for this "
|
|
494
|
+
"project; dense search is off in this one")
|
|
147
495
|
persist_dir.mkdir(parents=True, exist_ok=True)
|
|
496
|
+
if not _retried and self._store_has_segments(persist_dir):
|
|
497
|
+
ok, why = self._probe_store_isolated(persist_dir)
|
|
498
|
+
if not ok:
|
|
499
|
+
log.warning(
|
|
500
|
+
"embedding store failed its isolated probe (%s); "
|
|
501
|
+
"quarantining and rebuilding", why)
|
|
502
|
+
self._quarantine_store(persist_dir)
|
|
503
|
+
persist_dir.mkdir(parents=True, exist_ok=True)
|
|
148
504
|
self._chroma_client = chromadb.PersistentClient(
|
|
149
505
|
path=str(persist_dir),
|
|
150
506
|
settings=Settings(anonymized_telemetry=False),
|
|
@@ -179,17 +535,90 @@ class EmbeddingIndex:
|
|
|
179
535
|
pass
|
|
180
536
|
gc.collect()
|
|
181
537
|
|
|
538
|
+
@staticmethod
|
|
539
|
+
def _store_has_segments(persist_dir: Path) -> bool:
|
|
540
|
+
"""True when the store holds at least one on-disk HNSW segment."""
|
|
541
|
+
try:
|
|
542
|
+
return any(persist_dir.glob("*/header.bin"))
|
|
543
|
+
except OSError:
|
|
544
|
+
return False
|
|
545
|
+
|
|
546
|
+
@staticmethod
|
|
547
|
+
def _store_signature(persist_dir: Path) -> dict:
|
|
548
|
+
sig = {}
|
|
549
|
+
for f in sorted(persist_dir.rglob("*")):
|
|
550
|
+
try:
|
|
551
|
+
if f.is_file():
|
|
552
|
+
st = f.stat()
|
|
553
|
+
sig[f.relative_to(persist_dir).as_posix()] = [st.st_size, st.st_mtime_ns]
|
|
554
|
+
except OSError:
|
|
555
|
+
continue
|
|
556
|
+
return sig
|
|
557
|
+
|
|
558
|
+
def _probe_store_isolated(self, persist_dir: Path) -> tuple:
|
|
559
|
+
"""Open the store in a capped child first; skip if unchanged since a pass.
|
|
560
|
+
|
|
561
|
+
Returns ``(ok, reason)``. A pass is remembered against the files' sizes
|
|
562
|
+
and mtimes, so an untouched store is not re-probed on every start.
|
|
563
|
+
"""
|
|
564
|
+
sig = self._store_signature(persist_dir)
|
|
565
|
+
ok_file = self._index_dir / _PROBE_OK_FILE
|
|
566
|
+
try:
|
|
567
|
+
if json.loads(ok_file.read_text(encoding="utf-8")) == sig:
|
|
568
|
+
return True, ""
|
|
569
|
+
except Exception:
|
|
570
|
+
pass
|
|
571
|
+
store_bytes = sum(v[0] for v in sig.values())
|
|
572
|
+
cap = max(_PROBE_MIN_CAP_BYTES, _PROBE_CAP_STORE_MULTIPLE * store_bytes)
|
|
573
|
+
started = time.monotonic()
|
|
574
|
+
try:
|
|
575
|
+
ok, why = run_isolated_probe(
|
|
576
|
+
["-c", _PROBE_CHILD_CODE, str(persist_dir), COLLECTION_NAME],
|
|
577
|
+
cap_bytes=cap,
|
|
578
|
+
)
|
|
579
|
+
except Exception as e: # could not spawn: the in-process probe still runs
|
|
580
|
+
log.warning("isolated store probe could not run (%s); skipping", e)
|
|
581
|
+
return True, ""
|
|
582
|
+
log.info("isolated store probe: ok=%s in %.1fs %s",
|
|
583
|
+
ok, time.monotonic() - started, why)
|
|
584
|
+
if ok:
|
|
585
|
+
try:
|
|
586
|
+
# The child may have compacted the store; record what it left.
|
|
587
|
+
ok_file.write_text(json.dumps(self._store_signature(persist_dir)),
|
|
588
|
+
encoding="utf-8")
|
|
589
|
+
except OSError:
|
|
590
|
+
pass
|
|
591
|
+
return ok, why
|
|
592
|
+
|
|
593
|
+
def _prune_quarantines(self) -> None:
|
|
594
|
+
prune_quarantines(self._index_dir)
|
|
595
|
+
|
|
182
596
|
def _quarantine_store(self, persist_dir: Path) -> None:
|
|
183
597
|
"""Move a corrupt store and its hash file aside for post-mortem.
|
|
184
598
|
|
|
185
599
|
The hashes go with it: they claim vectors that the fresh store does not
|
|
186
600
|
have, so leaving them behind would suppress the very rebuild this is
|
|
187
601
|
meant to trigger.
|
|
602
|
+
|
|
603
|
+
Rename only, never copy. When another process still had the files open,
|
|
604
|
+
``shutil.move`` fell back to copying the whole store and then failed to
|
|
605
|
+
delete the original, so every session left one more full copy behind
|
|
606
|
+
and reopened the same corrupt store (issue #180). A failed rename now
|
|
607
|
+
raises and the index degrades for this process.
|
|
188
608
|
"""
|
|
189
609
|
self._close_chroma()
|
|
190
|
-
dest = self._index_dir / f"quarantine_corrupt_{datetime.now():%Y%m%d_%H%M%
|
|
610
|
+
dest = self._index_dir / f"quarantine_corrupt_{datetime.now():%Y%m%d_%H%M%S_%f}"
|
|
191
611
|
dest.mkdir(parents=True, exist_ok=True)
|
|
192
|
-
|
|
612
|
+
try:
|
|
613
|
+
os.rename(str(persist_dir), str(dest / persist_dir.name))
|
|
614
|
+
except OSError as e:
|
|
615
|
+
try:
|
|
616
|
+
dest.rmdir()
|
|
617
|
+
except OSError:
|
|
618
|
+
pass
|
|
619
|
+
raise RuntimeError(
|
|
620
|
+
f"could not move the corrupt embedding store aside ({e})") from e
|
|
621
|
+
(self._index_dir / _PROBE_OK_FILE).unlink(missing_ok=True)
|
|
193
622
|
try:
|
|
194
623
|
if self._hash_file.exists():
|
|
195
624
|
shutil.move(str(self._hash_file), str(dest / self._hash_file.name))
|
|
@@ -197,6 +626,7 @@ class EmbeddingIndex:
|
|
|
197
626
|
self._hash_file.unlink(missing_ok=True)
|
|
198
627
|
self._file_hashes = {}
|
|
199
628
|
log.warning("quarantined corrupt embedding store to %s", dest)
|
|
629
|
+
self._prune_quarantines()
|
|
200
630
|
|
|
201
631
|
def _drop_legacy_collection(self) -> None:
|
|
202
632
|
"""Best-effort removal of the pre-2.108.0 collection and hash file.
|
|
@@ -290,7 +720,7 @@ class EmbeddingIndex:
|
|
|
290
720
|
def unavailable_reason(self) -> str:
|
|
291
721
|
"""Human-readable reason ``ready`` is False (after probe/init)."""
|
|
292
722
|
if not self._available:
|
|
293
|
-
return "chromadb not installed"
|
|
723
|
+
return self._unavailable_why or "chromadb not installed"
|
|
294
724
|
if not self._ollama_up:
|
|
295
725
|
return "Ollama not reachable"
|
|
296
726
|
if not self._model_ok:
|