code-context-control 2.66.0__tar.gz → 2.70.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.66.0/code_context_control.egg-info → code_context_control-2.70.0}/PKG-INFO +55 -3
- {code_context_control-2.66.0 → code_context_control-2.70.0}/README.md +54 -2
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/c3.py +296 -1
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/parser.py +102 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_access_guard.py +20 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_pretool_enforce.py +22 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_server.py +178 -55
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_panel.js +2 -0
- code_context_control-2.70.0/cli/hub_ui/components/hub_enforcement.js +1162 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/mcp_server.py +24 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/server.py +18 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/delegate.py +78 -35
- code_context_control-2.70.0/cli/tools/override.py +173 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0/code_context_control.egg-info}/PKG-INFO +55 -3
- {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/SOURCES.txt +12 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/config.py +21 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/oracle_server.py +11 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/oracle_ui.html +19 -0
- code_context_control-2.70.0/oracle/services/mobile_api.py +1618 -0
- code_context_control-2.70.0/oracle/ui/qrcode.js +2297 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/settings.js +43 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/pyproject.toml +1 -1
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/access_guard.py +32 -1
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/access_telemetry.py +48 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/credential_store.py +37 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/embedding_index.py +79 -10
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/enforcement_policy.py +140 -30
- code_context_control-2.70.0/services/override_grants.py +511 -0
- code_context_control-2.70.0/services/override_policy.py +391 -0
- code_context_control-2.70.0/services/override_requests.py +379 -0
- code_context_control-2.70.0/tests/test_delegate_version_probe.py +247 -0
- code_context_control-2.70.0/tests/test_embedding_index_deadlock.py +321 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_policy.py +245 -4
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_routes.py +29 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_enforcement_routes.py +213 -0
- code_context_control-2.70.0/tests/test_mobile_api.py +370 -0
- code_context_control-2.70.0/tests/test_mobile_security_api.py +719 -0
- code_context_control-2.70.0/tests/test_override_grants.py +442 -0
- code_context_control-2.70.0/tests/test_override_requests.py +329 -0
- code_context_control-2.66.0/cli/hub_ui/components/hub_enforcement.js +0 -342
- {code_context_control-2.66.0 → code_context_control-2.70.0}/LICENSE +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/common.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/docs.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/edits.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/access.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/index.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_read.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/progress.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/project.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/read.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/search.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/session.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/status.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/api.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/app.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/core/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/core/config.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/core/ide.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/core/web_security.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/activity_log.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agent_base.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agent_locks.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agents.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/artifact_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/auto_memory.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/claude_md.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/compressor.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/conversation_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/doc_index.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/error_reporting.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/file_memory.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/git_context.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/hub_service.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/indexer.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_client.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_links.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_activation.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_presets.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_graph.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_queue.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/metrics.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/notifications.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_client.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/output_filter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/parser.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/project_manager.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/project_runtime.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/protocol.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/proxy_state.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/repo_map.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/retention.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/router.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/runtime.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/scanner.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_manager.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_preloader.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/subprojects.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/task_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/telemetry.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/text_index.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/time_tracker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/transcript_index.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/validation_cache.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/vector_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/watcher.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/setup.cfg +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_retention.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_validate.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/backend.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/main.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/theme.tcss +0 -0
{code_context_control-2.66.0/code_context_control.egg-info → code_context_control-2.70.0}/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.70.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
|
|
@@ -186,7 +186,7 @@ Twelve tabs. The dashboard above shows token savings, indexed files, the live se
|
|
|
186
186
|
|
|
187
187
|
## The MCP tool suite
|
|
188
188
|
|
|
189
|
-
C3 exposes **
|
|
189
|
+
C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
|
|
190
190
|
|
|
191
191
|
| Tool | What it does |
|
|
192
192
|
|---|---|
|
|
@@ -201,6 +201,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
|
|
|
201
201
|
| `c3_memory` | Fact store with categories, recall, graph queries, `index`→`fetch` two-step |
|
|
202
202
|
| `c3_session` | Snapshot, restore, log decisions, compact history |
|
|
203
203
|
| `c3_impact` | Blast-radius analysis before editing shared symbols |
|
|
204
|
+
| `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
|
|
204
205
|
| `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
|
|
205
206
|
| `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
|
|
206
207
|
| `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter |
|
|
@@ -211,7 +212,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
|
|
|
211
212
|
| `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
|
|
212
213
|
| `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
|
|
213
214
|
|
|
214
|
-
Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task`.
|
|
215
|
+
Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
|
|
215
216
|
|
|
216
217
|
On Windows `c3_shell` uses Git Bash when available. Git Bash bundles no `jq`; use `python -m json.tool` for portable JSON formatting.
|
|
217
218
|
|
|
@@ -242,12 +243,61 @@ c3 access mask activate # purge pre-mask artifacts, buil
|
|
|
242
243
|
- **Masked means read-only, always.** Cropped rows have no inverse, so an edit in transformed coordinates would corrupt the one file you protected.
|
|
243
244
|
- **Four deterministic presets, no LLM in the read path:** `redact_secrets`, `redact_columns` (salted one-way pseudonyms — joins survive, no reverse dictionary), `sample_rows`, `signatures_only`.
|
|
244
245
|
- **Rule changes are human-only** (UI tab or CLI) and ledger-logged. Agents have no mutation surface.
|
|
246
|
+
- **Built-ins can be switched off when you need to** (v2.65.0). `**/.env*`, `**/.c3/**`, `**/.claude/settings*.json` and `**/.git/**` yield to `c3 access builtin disable <glob>`, which makes you retype the glob first. It takes **two keys** — a config entry *and* a keyring attestation — so an agent that manages to write `config.json` still cannot grant itself write access to your settings. The credential vault stays absolute.
|
|
245
247
|
- **Honest coverage.** This guards *cooperative* agents against mistakes and prompt injection. It is not a sandbox — a raw shell outside C3's tools still sees the real bytes.
|
|
246
248
|
|
|
247
249
|
→ [Access Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/access-guard.md) · [Mask Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/mask-guard.md)
|
|
248
250
|
|
|
249
251
|
---
|
|
250
252
|
|
|
253
|
+
## Agent Locks — several agents, one repo (v2.65.0)
|
|
254
|
+
|
|
255
|
+
Guards answer *may the agent touch this?* Locks answer *is someone already touching it?*
|
|
256
|
+
|
|
257
|
+
Two agents editing one file used to be silent data loss. `c3_edit` serialized
|
|
258
|
+
same-file edits with an in-process lock, but every session runs its own `c3-mcp`
|
|
259
|
+
server — so two sessions could interleave read → replace → write and lose an edit
|
|
260
|
+
with no error on either side. Create mode was worse: both agents "succeeded" and
|
|
261
|
+
one file won.
|
|
262
|
+
|
|
263
|
+
Now there are two mechanisms, because they solve different problems:
|
|
264
|
+
|
|
265
|
+
- **Torn writes** — a cross-process file lock, held across create, single-edit and
|
|
266
|
+
batch alike. No daemon, no configuration, nothing to switch on.
|
|
267
|
+
- **Overlapping work** — a *lease*. `c3_edit` takes one on the file it edits,
|
|
268
|
+
carrying the intent from your edit summary. A second agent gets told who holds
|
|
269
|
+
it and why:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
[c3-lock:held] services/router.py is held by claude-code:a3f19c2b.
|
|
273
|
+
intent: "refactor retry backoff"
|
|
274
|
+
lease expires in 6m12s.
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
c3 locks list # who holds what, across the project
|
|
279
|
+
c3 locks force-release services/router.py # break a stuck lease (human-only, audited)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- **TTL is the real release mechanism.** Agents forget to release, so nothing
|
|
283
|
+
assumes they will — a crashed agent can never wedge a repo.
|
|
284
|
+
- **Acquisition is all-or-nothing** over a sorted path list, so two agents grabbing
|
|
285
|
+
the same pair in opposite order cannot deadlock.
|
|
286
|
+
- **`force-release` bumps a fencing counter**, so a holder that comes back is stale
|
|
287
|
+
by construction rather than by hope. It lives in the CLI and the Hub, never in
|
|
288
|
+
the agent-facing tool.
|
|
289
|
+
- **The Hub's Locks tab badges a project it cannot read as `UNREADABLE`**, not as
|
|
290
|
+
zero leases — "all clear" is a different claim from "we don't know".
|
|
291
|
+
- **Honest coverage, again.** Leases gate C3's own tool surfaces. A raw `c3_shell`
|
|
292
|
+
redirect, a non-Claude agent, or a human in an editor is *not* covered, and the
|
|
293
|
+
UI says so on screen.
|
|
294
|
+
|
|
295
|
+
Set `locks.enabled: false` in `.c3/config.json` to opt out.
|
|
296
|
+
|
|
297
|
+
→ [Agent Locks](https://github.com/drknowhow/code-context-control/blob/main/docs/agent-locks.md)
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
251
301
|
## Integrations
|
|
252
302
|
|
|
253
303
|
| | What you get | Guide |
|
|
@@ -319,6 +369,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
319
369
|
## Security & privacy
|
|
320
370
|
|
|
321
371
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
372
|
+
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
373
|
+
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
|
|
322
374
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
323
375
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
324
376
|
- **API keys** for third-party providers are read from the environment and never persisted by C3.
|
|
@@ -123,7 +123,7 @@ Twelve tabs. The dashboard above shows token savings, indexed files, the live se
|
|
|
123
123
|
|
|
124
124
|
## The MCP tool suite
|
|
125
125
|
|
|
126
|
-
C3 exposes **
|
|
126
|
+
C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
|
|
127
127
|
|
|
128
128
|
| Tool | What it does |
|
|
129
129
|
|---|---|
|
|
@@ -138,6 +138,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
|
|
|
138
138
|
| `c3_memory` | Fact store with categories, recall, graph queries, `index`→`fetch` two-step |
|
|
139
139
|
| `c3_session` | Snapshot, restore, log decisions, compact history |
|
|
140
140
|
| `c3_impact` | Blast-radius analysis before editing shared symbols |
|
|
141
|
+
| `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
|
|
141
142
|
| `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
|
|
142
143
|
| `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
|
|
143
144
|
| `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter |
|
|
@@ -148,7 +149,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
|
|
|
148
149
|
| `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
|
|
149
150
|
| `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
|
|
150
151
|
|
|
151
|
-
Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task`.
|
|
152
|
+
Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
|
|
152
153
|
|
|
153
154
|
On Windows `c3_shell` uses Git Bash when available. Git Bash bundles no `jq`; use `python -m json.tool` for portable JSON formatting.
|
|
154
155
|
|
|
@@ -179,12 +180,61 @@ c3 access mask activate # purge pre-mask artifacts, buil
|
|
|
179
180
|
- **Masked means read-only, always.** Cropped rows have no inverse, so an edit in transformed coordinates would corrupt the one file you protected.
|
|
180
181
|
- **Four deterministic presets, no LLM in the read path:** `redact_secrets`, `redact_columns` (salted one-way pseudonyms — joins survive, no reverse dictionary), `sample_rows`, `signatures_only`.
|
|
181
182
|
- **Rule changes are human-only** (UI tab or CLI) and ledger-logged. Agents have no mutation surface.
|
|
183
|
+
- **Built-ins can be switched off when you need to** (v2.65.0). `**/.env*`, `**/.c3/**`, `**/.claude/settings*.json` and `**/.git/**` yield to `c3 access builtin disable <glob>`, which makes you retype the glob first. It takes **two keys** — a config entry *and* a keyring attestation — so an agent that manages to write `config.json` still cannot grant itself write access to your settings. The credential vault stays absolute.
|
|
182
184
|
- **Honest coverage.** This guards *cooperative* agents against mistakes and prompt injection. It is not a sandbox — a raw shell outside C3's tools still sees the real bytes.
|
|
183
185
|
|
|
184
186
|
→ [Access Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/access-guard.md) · [Mask Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/mask-guard.md)
|
|
185
187
|
|
|
186
188
|
---
|
|
187
189
|
|
|
190
|
+
## Agent Locks — several agents, one repo (v2.65.0)
|
|
191
|
+
|
|
192
|
+
Guards answer *may the agent touch this?* Locks answer *is someone already touching it?*
|
|
193
|
+
|
|
194
|
+
Two agents editing one file used to be silent data loss. `c3_edit` serialized
|
|
195
|
+
same-file edits with an in-process lock, but every session runs its own `c3-mcp`
|
|
196
|
+
server — so two sessions could interleave read → replace → write and lose an edit
|
|
197
|
+
with no error on either side. Create mode was worse: both agents "succeeded" and
|
|
198
|
+
one file won.
|
|
199
|
+
|
|
200
|
+
Now there are two mechanisms, because they solve different problems:
|
|
201
|
+
|
|
202
|
+
- **Torn writes** — a cross-process file lock, held across create, single-edit and
|
|
203
|
+
batch alike. No daemon, no configuration, nothing to switch on.
|
|
204
|
+
- **Overlapping work** — a *lease*. `c3_edit` takes one on the file it edits,
|
|
205
|
+
carrying the intent from your edit summary. A second agent gets told who holds
|
|
206
|
+
it and why:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
[c3-lock:held] services/router.py is held by claude-code:a3f19c2b.
|
|
210
|
+
intent: "refactor retry backoff"
|
|
211
|
+
lease expires in 6m12s.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
c3 locks list # who holds what, across the project
|
|
216
|
+
c3 locks force-release services/router.py # break a stuck lease (human-only, audited)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- **TTL is the real release mechanism.** Agents forget to release, so nothing
|
|
220
|
+
assumes they will — a crashed agent can never wedge a repo.
|
|
221
|
+
- **Acquisition is all-or-nothing** over a sorted path list, so two agents grabbing
|
|
222
|
+
the same pair in opposite order cannot deadlock.
|
|
223
|
+
- **`force-release` bumps a fencing counter**, so a holder that comes back is stale
|
|
224
|
+
by construction rather than by hope. It lives in the CLI and the Hub, never in
|
|
225
|
+
the agent-facing tool.
|
|
226
|
+
- **The Hub's Locks tab badges a project it cannot read as `UNREADABLE`**, not as
|
|
227
|
+
zero leases — "all clear" is a different claim from "we don't know".
|
|
228
|
+
- **Honest coverage, again.** Leases gate C3's own tool surfaces. A raw `c3_shell`
|
|
229
|
+
redirect, a non-Claude agent, or a human in an editor is *not* covered, and the
|
|
230
|
+
UI says so on screen.
|
|
231
|
+
|
|
232
|
+
Set `locks.enabled: false` in `.c3/config.json` to opt out.
|
|
233
|
+
|
|
234
|
+
→ [Agent Locks](https://github.com/drknowhow/code-context-control/blob/main/docs/agent-locks.md)
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
188
238
|
## Integrations
|
|
189
239
|
|
|
190
240
|
| | What you get | Guide |
|
|
@@ -256,6 +306,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
256
306
|
## Security & privacy
|
|
257
307
|
|
|
258
308
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
309
|
+
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
310
|
+
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
|
|
259
311
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
260
312
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
261
313
|
- **API keys** for third-party providers are read from the environment and never persisted by C3.
|
|
@@ -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.
|
|
95
|
+
__version__ = "2.70.0"
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def _compress_file_cli(compressor, path, mode="smart", **kw):
|
|
@@ -261,6 +261,7 @@ _C3_MCP_ALLOW = [
|
|
|
261
261
|
"mcp__c3__c3_bitbucket",
|
|
262
262
|
"mcp__c3__c3_jira", "mcp__c3__c3_credentials",
|
|
263
263
|
"mcp__c3__c3_project", "mcp__c3__c3_task", "mcp__c3__c3_artifacts",
|
|
264
|
+
"mcp__c3__c3_override",
|
|
264
265
|
]
|
|
265
266
|
|
|
266
267
|
# Obsolete MCP tool names from earlier C3 versions. `c3 permissions clean`
|
|
@@ -6209,6 +6210,299 @@ def _enforce_audit(result: dict, project_path: str) -> None:
|
|
|
6209
6210
|
pass
|
|
6210
6211
|
|
|
6211
6212
|
|
|
6213
|
+
_OVERRIDE_DEFAULT_TOOL = {"read": "Read", "write": "Edit"}
|
|
6214
|
+
|
|
6215
|
+
|
|
6216
|
+
def cmd_override(args):
|
|
6217
|
+
"""`c3 override` — Override Requests: allow one blocked call, once.
|
|
6218
|
+
|
|
6219
|
+
The human half of docs/override-requests.md. Every verb here mints or
|
|
6220
|
+
inspects a *grant*; none of them edits policy. That separation is the
|
|
6221
|
+
entire point of the feature: today the only way to unblock an agent is to
|
|
6222
|
+
weaken a rule permanently, and a guard that spends its life in `advisory`
|
|
6223
|
+
is not a guard.
|
|
6224
|
+
|
|
6225
|
+
No agent surface reaches this command, and no approval here can touch the
|
|
6226
|
+
'never' rows of spec §2 — the credential vault, Tier-0 absolute denies, or
|
|
6227
|
+
the catastrophic shell blocks.
|
|
6228
|
+
"""
|
|
6229
|
+
sub = getattr(args, "override_cmd", None)
|
|
6230
|
+
if not sub:
|
|
6231
|
+
print("Usage: c3 override <policy|grant|list|revoke|check|sweep> ...")
|
|
6232
|
+
print(" c3 override policy # what is escalatable right now")
|
|
6233
|
+
return
|
|
6234
|
+
|
|
6235
|
+
from services import override_grants as og
|
|
6236
|
+
from services import override_policy as opol
|
|
6237
|
+
|
|
6238
|
+
project_path = str(Path(getattr(args, "project_path", ".") or ".").resolve())
|
|
6239
|
+
policy = opol.resolve(project_path)
|
|
6240
|
+
for warn in policy.warnings:
|
|
6241
|
+
print(f" [warn] {warn}")
|
|
6242
|
+
|
|
6243
|
+
if sub == "policy":
|
|
6244
|
+
_override_show(policy, project_path, opol)
|
|
6245
|
+
elif sub == "grant":
|
|
6246
|
+
_override_grant(args, policy, project_path, og, opol)
|
|
6247
|
+
elif sub == "list":
|
|
6248
|
+
_override_list(args, project_path, og)
|
|
6249
|
+
elif sub == "check":
|
|
6250
|
+
_override_check(args, policy, project_path, og, opol)
|
|
6251
|
+
elif sub == "revoke":
|
|
6252
|
+
gid = getattr(args, "grant_id", "")
|
|
6253
|
+
print(" Revoked." if og.revoke(project_path, gid)
|
|
6254
|
+
else f" No live grant with id '{gid}'.")
|
|
6255
|
+
elif sub == "sweep":
|
|
6256
|
+
print(f" Dropped {og.sweep_expired(project_path)} expired/spent grant(s).")
|
|
6257
|
+
elif sub == "requests":
|
|
6258
|
+
_override_requests(args, project_path)
|
|
6259
|
+
elif sub in ("approve", "deny"):
|
|
6260
|
+
_override_decide(args, sub, project_path)
|
|
6261
|
+
|
|
6262
|
+
|
|
6263
|
+
def _override_requests(args, project_path: str) -> None:
|
|
6264
|
+
from services import override_requests as orq
|
|
6265
|
+
|
|
6266
|
+
rows = orq.list_requests(
|
|
6267
|
+
project_path="" if getattr(args, "all_projects", False) else project_path,
|
|
6268
|
+
status=getattr(args, "status", "") or "")
|
|
6269
|
+
print_header("Override requests")
|
|
6270
|
+
if not rows:
|
|
6271
|
+
print(" (none)")
|
|
6272
|
+
return
|
|
6273
|
+
for r in rows:
|
|
6274
|
+
note = f" note: {r['decision_note']}" if r.get("decision_note") else ""
|
|
6275
|
+
print(f" {r['id']} {r['status']:<9} {r['tool']} {r['op']} {r['path']}")
|
|
6276
|
+
print(f" {'':18s} rule {r['rule']} ({r['rule_class']}) · session "
|
|
6277
|
+
f"{r['session_id']} · expires {r['expires_at']}{note}")
|
|
6278
|
+
if r.get("justification"):
|
|
6279
|
+
print(f" {'':18s} the agent wrote: \"{r['justification']}\"")
|
|
6280
|
+
print(f" {'':18s} (untrusted — it may be repeating text it read "
|
|
6281
|
+
f"from a file)")
|
|
6282
|
+
print("\n Approve: c3 override approve <id> Refuse: c3 override deny <id>")
|
|
6283
|
+
|
|
6284
|
+
|
|
6285
|
+
def _override_decide(args, decision: str, project_path: str) -> None:
|
|
6286
|
+
from services import override_requests as orq
|
|
6287
|
+
|
|
6288
|
+
try:
|
|
6289
|
+
row = orq.decide(getattr(args, "request_id", ""), decision,
|
|
6290
|
+
uses=getattr(args, "uses", None),
|
|
6291
|
+
ttl_s=getattr(args, "ttl_s", None),
|
|
6292
|
+
note=getattr(args, "note", "") or "",
|
|
6293
|
+
decided_by="cli",
|
|
6294
|
+
confirm=getattr(args, "confirm", None))
|
|
6295
|
+
except orq.OverrideError as exc:
|
|
6296
|
+
print(f" {exc}")
|
|
6297
|
+
return
|
|
6298
|
+
except ValueError as exc:
|
|
6299
|
+
print(f" Error: {exc}")
|
|
6300
|
+
return
|
|
6301
|
+
|
|
6302
|
+
if decision == "deny":
|
|
6303
|
+
print(f" Denied {row['id']}. The block stands.")
|
|
6304
|
+
return
|
|
6305
|
+
print_header("Override approved")
|
|
6306
|
+
print(f" Request : {row['id']} Grant: {row.get('grant_id', '?')}")
|
|
6307
|
+
print(f" Allows : {row['tool']} {row['op']} on {row['path']}")
|
|
6308
|
+
print(f" Session : {row['session_id']}")
|
|
6309
|
+
print(f"\n The rule {row['rule']} is still in force. One retry of this "
|
|
6310
|
+
"exact call, in")
|
|
6311
|
+
print(" that session, on that exact path — nothing else.")
|
|
6312
|
+
|
|
6313
|
+
|
|
6314
|
+
def _override_show(policy, project_path: str, opol) -> None:
|
|
6315
|
+
print_header("Override Requests")
|
|
6316
|
+
state = "on" if policy.enabled else "off"
|
|
6317
|
+
print(f" Enabled : {state} (channel: {policy.channel})")
|
|
6318
|
+
print(f" Project : {project_path}")
|
|
6319
|
+
print("\n Escalatable layers:")
|
|
6320
|
+
for key in opol.LAYER_KEYS:
|
|
6321
|
+
on = policy.escalatable(key)
|
|
6322
|
+
mark = "*" if on else " "
|
|
6323
|
+
extra = " (approval needs the rule typed by hand)" \
|
|
6324
|
+
if key in opol.TYPED_CONFIRM_LAYERS else ""
|
|
6325
|
+
print(f" {mark} {key:18s} {'yes' if on else 'no':3s}{extra}")
|
|
6326
|
+
print("\n Limits:")
|
|
6327
|
+
print(f" grant TTL : <= {policy.max_ttl_s}s (hard ceiling "
|
|
6328
|
+
f"{opol.HARD_MAX_TTL_S}s)")
|
|
6329
|
+
print(f" uses : {policy.default_uses} "
|
|
6330
|
+
f"(session grants: {'allowed' if policy.allow_session_grants else 'off'})")
|
|
6331
|
+
print(f" request TTL : {policy.request_ttl_s}s")
|
|
6332
|
+
print(f" rate : {policy.max_pending_per_session} pending/session, "
|
|
6333
|
+
f"{policy.max_requests_per_hour}/hour")
|
|
6334
|
+
if policy.corrupt_scopes:
|
|
6335
|
+
print(f"\n FAILING CLOSED — corrupt scope(s): "
|
|
6336
|
+
f"{', '.join(policy.corrupt_scopes)}")
|
|
6337
|
+
print("\n Never escalatable, at any setting: the credential vault, "
|
|
6338
|
+
".c3/secrets.enc,")
|
|
6339
|
+
print(" .c3/cred_state.json, the dispatcher fail-closed deny, and the "
|
|
6340
|
+
"catastrophic")
|
|
6341
|
+
print(" shell blocks. Policy lives in .c3/config.json 'override' "
|
|
6342
|
+
"(human-edited).")
|
|
6343
|
+
|
|
6344
|
+
|
|
6345
|
+
def _override_identify(args, project_path: str, opol):
|
|
6346
|
+
"""(gate, layer_key, rule, tool, op, error) for the call being approved.
|
|
6347
|
+
|
|
6348
|
+
Derived from the denial the path ACTUALLY produces, not from what the
|
|
6349
|
+
caller claims: a grant whose rule string does not match the rule that
|
|
6350
|
+
denies would never match at consume time, and a silently-useless grant is
|
|
6351
|
+
worse than a refusal.
|
|
6352
|
+
"""
|
|
6353
|
+
op = getattr(args, "op", "read") or "read"
|
|
6354
|
+
tool = getattr(args, "tool", None) or _OVERRIDE_DEFAULT_TOOL[op]
|
|
6355
|
+
layer = getattr(args, "layer", None)
|
|
6356
|
+
target = getattr(args, "target", "")
|
|
6357
|
+
|
|
6358
|
+
if layer == "discipline":
|
|
6359
|
+
return (opol.GATE_DISCIPLINE, opol.LAYER_DISCIPLINE,
|
|
6360
|
+
opol.RULE_DISCIPLINE, tool, "write", None)
|
|
6361
|
+
if layer == "shell":
|
|
6362
|
+
return (opol.GATE_SHELL, opol.LAYER_SHELL_WARN,
|
|
6363
|
+
opol.RULE_SHELL_WARN, tool, op, None)
|
|
6364
|
+
|
|
6365
|
+
from services import access_guard as ag
|
|
6366
|
+
denial = ag.check(target, op, project_path)
|
|
6367
|
+
if denial is None:
|
|
6368
|
+
return (None, None, None, tool, op,
|
|
6369
|
+
f"`{target}` is not blocked for {op} — nothing to approve.")
|
|
6370
|
+
layer_key = opol.rule_class_for_denial(denial)
|
|
6371
|
+
if layer_key is None:
|
|
6372
|
+
return (None, None, None, tool, op,
|
|
6373
|
+
f"{opol.TAG_NOT_ESCALATABLE} `{denial.rule}` can never be "
|
|
6374
|
+
f"overridden — no config value and no approval reaches it.")
|
|
6375
|
+
gate = opol.GATE_FOR_LAYER_KEY[layer_key]
|
|
6376
|
+
if layer and layer != gate:
|
|
6377
|
+
return (None, None, None, tool, op,
|
|
6378
|
+
f"--layer {layer} does not match the layer that denied "
|
|
6379
|
+
f"({gate}, rule `{denial.rule}`).")
|
|
6380
|
+
return gate, layer_key, denial.rule, tool, op, None
|
|
6381
|
+
|
|
6382
|
+
|
|
6383
|
+
def _override_grant(args, policy, project_path: str, og, opol) -> None:
|
|
6384
|
+
gate, layer_key, rule, tool, op, err = _override_identify(
|
|
6385
|
+
args, project_path, opol)
|
|
6386
|
+
if err:
|
|
6387
|
+
print(f" {err}")
|
|
6388
|
+
return
|
|
6389
|
+
if not policy.enabled:
|
|
6390
|
+
print(" Overrides are disabled for this project.")
|
|
6391
|
+
print(" Enable by hand in .c3/config.json — the 'override' section, "
|
|
6392
|
+
"\"enabled\": true,")
|
|
6393
|
+
print(f" plus \"layers\": {{\"{layer_key}\": true}}. Nothing is "
|
|
6394
|
+
"escalatable by default.")
|
|
6395
|
+
return
|
|
6396
|
+
if not policy.escalatable(layer_key):
|
|
6397
|
+
print(f" The '{layer_key}' layer is not escalatable "
|
|
6398
|
+
f"(override.layers.{layer_key} is false).")
|
|
6399
|
+
print(" Opt in per layer, deliberately — that is the point of the "
|
|
6400
|
+
"default.")
|
|
6401
|
+
return
|
|
6402
|
+
if (layer_key in opol.TYPED_CONFIRM_LAYERS
|
|
6403
|
+
and getattr(args, "confirm", None) != rule):
|
|
6404
|
+
print(f" This approves ONE call against an {layer_key} rule "
|
|
6405
|
+
f"({rule}).")
|
|
6406
|
+
print(f" Retype the rule to confirm: --confirm '{rule}'")
|
|
6407
|
+
return
|
|
6408
|
+
try:
|
|
6409
|
+
grant = og.mint(project_path, session_id=getattr(args, "session_id", ""),
|
|
6410
|
+
layer=gate, rule=rule, tool=tool, op=op,
|
|
6411
|
+
path=getattr(args, "target", ""),
|
|
6412
|
+
ttl_s=getattr(args, "ttl_s", None),
|
|
6413
|
+
uses=getattr(args, "uses", None),
|
|
6414
|
+
granted_by="cli", policy=policy)
|
|
6415
|
+
except ValueError as exc:
|
|
6416
|
+
print(f" Error: {exc}")
|
|
6417
|
+
return
|
|
6418
|
+
|
|
6419
|
+
print_header("Override granted")
|
|
6420
|
+
print(f" Id : {grant['id']}")
|
|
6421
|
+
print(f" Allows : {tool} {op} on {grant['path_key']}")
|
|
6422
|
+
print(f" Layer : {gate} Rule: {rule}")
|
|
6423
|
+
print(f" Session : {grant['session_id']}")
|
|
6424
|
+
print(f" Expires : {grant['expires_at']} Uses: "
|
|
6425
|
+
f"{grant['uses_remaining']}")
|
|
6426
|
+
if getattr(args, "ttl_s", None) and grant.get("expires_at"):
|
|
6427
|
+
print(f" (TTL clamped to override.max_ttl_s = {policy.max_ttl_s}s "
|
|
6428
|
+
f"where it exceeded it)")
|
|
6429
|
+
print(f"\n The rule {rule} is still in force. This grant authorises one "
|
|
6430
|
+
"retry of")
|
|
6431
|
+
print(" this exact call, in this session, on this exact path, and "
|
|
6432
|
+
"nothing else.")
|
|
6433
|
+
_override_audit(grant, project_path)
|
|
6434
|
+
|
|
6435
|
+
|
|
6436
|
+
def _override_list(args, project_path: str, og) -> None:
|
|
6437
|
+
grants = og.active(project_path, getattr(args, "session_id", "") or "")
|
|
6438
|
+
print_header("Live override grants")
|
|
6439
|
+
if not grants:
|
|
6440
|
+
print(" (none)")
|
|
6441
|
+
for g in grants:
|
|
6442
|
+
print(f" {g.get('id', '?'):18s} {g.get('tool', '?')} "
|
|
6443
|
+
f"{g.get('op', '?')} {g.get('path_key', '?')}")
|
|
6444
|
+
print(f" {'':18s} rule {g.get('rule', '?')} · session "
|
|
6445
|
+
f"{g.get('session_id', '?')} · expires {g.get('expires_at', '?')}"
|
|
6446
|
+
f" · {g.get('uses_remaining', 0)} use(s) left")
|
|
6447
|
+
tail = int(getattr(args, "audit", 0) or 0)
|
|
6448
|
+
if tail:
|
|
6449
|
+
print("\n Audit (.c3/overrides.jsonl):")
|
|
6450
|
+
for line in og.read_audit(project_path, tail):
|
|
6451
|
+
print(f" {line.get('ts', '?')} {line.get('event', '?'):12s} "
|
|
6452
|
+
f"{line.get('grant_id', '')} {line.get('path', '')}")
|
|
6453
|
+
|
|
6454
|
+
|
|
6455
|
+
def _override_check(args, policy, project_path: str, og, opol) -> None:
|
|
6456
|
+
gate, layer_key, rule, tool, op, err = _override_identify(
|
|
6457
|
+
args, project_path, opol)
|
|
6458
|
+
if err:
|
|
6459
|
+
print(f" {err}")
|
|
6460
|
+
return
|
|
6461
|
+
if not policy.escalatable(layer_key):
|
|
6462
|
+
print(f" Denied — the '{layer_key}' layer is not escalatable, so no "
|
|
6463
|
+
"grant is even consulted.")
|
|
6464
|
+
return
|
|
6465
|
+
hit = og.find(project_path, session_id=getattr(args, "session_id", ""),
|
|
6466
|
+
layer=gate, rule=rule, tool=tool, op=op,
|
|
6467
|
+
path=getattr(args, "target", ""))
|
|
6468
|
+
if hit:
|
|
6469
|
+
print(f" Allowed by {hit['id']} — {hit['uses_remaining']} use(s) "
|
|
6470
|
+
f"left, expires {hit['expires_at']}.")
|
|
6471
|
+
print(" (`check` never consumes a use.)")
|
|
6472
|
+
else:
|
|
6473
|
+
print(" Denied — no live grant matches this exact "
|
|
6474
|
+
"session/layer/rule/tool/op/path.")
|
|
6475
|
+
|
|
6476
|
+
|
|
6477
|
+
def _override_audit(grant: dict, project_path: str) -> None:
|
|
6478
|
+
"""Ledger + activity entry. The JSONL audit is written by the service."""
|
|
6479
|
+
try:
|
|
6480
|
+
from services.activity_log import ActivityLog
|
|
6481
|
+
ActivityLog(project_path).log("access_action", {
|
|
6482
|
+
"kind": "override", "action": "grant", "grant_id": grant["id"],
|
|
6483
|
+
"layer": grant["layer"], "rule": grant["rule"],
|
|
6484
|
+
"tool": grant["tool"], "op": grant["op"],
|
|
6485
|
+
"path": grant["path_key"], "session_id": grant["session_id"],
|
|
6486
|
+
"expires_at": grant["expires_at"],
|
|
6487
|
+
"uses": grant["uses_remaining"], "granted_by": grant["granted_by"],
|
|
6488
|
+
})
|
|
6489
|
+
except Exception:
|
|
6490
|
+
pass
|
|
6491
|
+
try:
|
|
6492
|
+
from services.edit_ledger import EditLedger
|
|
6493
|
+
EditLedger(project_path).log_edit(
|
|
6494
|
+
file=f"override://{grant['id']}",
|
|
6495
|
+
change_type="override_grant",
|
|
6496
|
+
summary=(f"granted {grant['tool']} {grant['op']} on "
|
|
6497
|
+
f"{grant['path_key']} once (rule {grant['rule']}, "
|
|
6498
|
+
f"expires {grant['expires_at']}) via `c3 override`"),
|
|
6499
|
+
tags=["override", "access"],
|
|
6500
|
+
detail={"kind": "override", **grant},
|
|
6501
|
+
)
|
|
6502
|
+
except Exception:
|
|
6503
|
+
pass
|
|
6504
|
+
|
|
6505
|
+
|
|
6212
6506
|
def cmd_access(args):
|
|
6213
6507
|
"""Access Guard rule management — human-only mutation surface (spec §1)."""
|
|
6214
6508
|
sub = getattr(args, "access_cmd", None)
|
|
@@ -7994,6 +8288,7 @@ def main():
|
|
|
7994
8288
|
"creds": cmd_creds,
|
|
7995
8289
|
"access": cmd_access,
|
|
7996
8290
|
"enforce": cmd_enforce,
|
|
8291
|
+
"override": cmd_override,
|
|
7997
8292
|
"locks": cmd_locks,
|
|
7998
8293
|
"oracle": cmd_oracle,
|
|
7999
8294
|
"upgrade": cmd_upgrade,
|
|
@@ -472,6 +472,108 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
472
472
|
lk_sweep = locks_subs.add_parser("sweep", help="Drop expired leases now")
|
|
473
473
|
lk_sweep.add_argument("--path", dest="project_path", default=".")
|
|
474
474
|
|
|
475
|
+
# ── Override Requests (v2.69.0, docs/override-requests.md) ──────────
|
|
476
|
+
# Human-only approval surface. A grant is single-use, session-bound,
|
|
477
|
+
# path-exact and TTL-capped, and it never edits policy: the rule that
|
|
478
|
+
# denied the call is still in force the moment the grant is spent.
|
|
479
|
+
# There is no agent-facing path to any verb in this command.
|
|
480
|
+
p_override = subparsers.add_parser(
|
|
481
|
+
"override",
|
|
482
|
+
help="Override Requests — allow one blocked call once, without weakening the rule",
|
|
483
|
+
)
|
|
484
|
+
override_subs = p_override.add_subparsers(dest="override_cmd")
|
|
485
|
+
|
|
486
|
+
ov_policy = override_subs.add_parser(
|
|
487
|
+
"policy", help="Show the effective `override` policy and which layers are escalatable"
|
|
488
|
+
)
|
|
489
|
+
ov_policy.add_argument("--path", dest="project_path", default=".",
|
|
490
|
+
help="Project directory (default: current)")
|
|
491
|
+
|
|
492
|
+
ov_grant = override_subs.add_parser(
|
|
493
|
+
"grant", help="Mint a single-use grant for one blocked call (human approval)"
|
|
494
|
+
)
|
|
495
|
+
ov_grant.add_argument("target", help="The exact path that was blocked")
|
|
496
|
+
ov_grant.add_argument("--session", dest="session_id", required=True,
|
|
497
|
+
help="Claude Code session id the grant is bound to — a grant never crosses sessions")
|
|
498
|
+
ov_grant.add_argument("--op", choices=["read", "write"], default="read",
|
|
499
|
+
help="Operation to allow (default: read)")
|
|
500
|
+
ov_grant.add_argument("--tool", default=None,
|
|
501
|
+
help="Exact tool name (default: Read for --op read, Edit for --op write)")
|
|
502
|
+
ov_grant.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
|
|
503
|
+
default=None,
|
|
504
|
+
help="Force a layer. Default: derive it from the denial the path actually produces")
|
|
505
|
+
ov_grant.add_argument("--ttl", type=int, default=None, dest="ttl_s",
|
|
506
|
+
help="Seconds until the grant expires (clamped to override.max_ttl_s, hard ceiling 900)")
|
|
507
|
+
ov_grant.add_argument("--uses", type=int, default=None,
|
|
508
|
+
help="Uses allowed (default 1; >1 needs override.allow_session_grants)")
|
|
509
|
+
ov_grant.add_argument("--confirm", default=None,
|
|
510
|
+
help="Required for deny/builtin rules: retype the rule glob by hand")
|
|
511
|
+
ov_grant.add_argument("--path", dest="project_path", default=".",
|
|
512
|
+
help="Project directory (default: current)")
|
|
513
|
+
|
|
514
|
+
ov_list = override_subs.add_parser("list", help="Live grants for this project")
|
|
515
|
+
ov_list.add_argument("--session", dest="session_id", default="",
|
|
516
|
+
help="Only grants bound to this session id")
|
|
517
|
+
ov_list.add_argument("--audit", type=int, default=0, metavar="N",
|
|
518
|
+
help="Also print the last N lines of .c3/overrides.jsonl")
|
|
519
|
+
ov_list.add_argument("--path", dest="project_path", default=".",
|
|
520
|
+
help="Project directory (default: current)")
|
|
521
|
+
|
|
522
|
+
ov_revoke = override_subs.add_parser("revoke", help="Drop a live grant before it is used")
|
|
523
|
+
ov_revoke.add_argument("grant_id", help="Grant id (grt_…)")
|
|
524
|
+
ov_revoke.add_argument("--path", dest="project_path", default=".",
|
|
525
|
+
help="Project directory (default: current)")
|
|
526
|
+
|
|
527
|
+
ov_check = override_subs.add_parser(
|
|
528
|
+
"check", help="Would a grant cover this call right now? (never consumes one)"
|
|
529
|
+
)
|
|
530
|
+
ov_check.add_argument("target", help="Path to test")
|
|
531
|
+
ov_check.add_argument("--session", dest="session_id", required=True)
|
|
532
|
+
ov_check.add_argument("--op", choices=["read", "write"], default="read")
|
|
533
|
+
ov_check.add_argument("--tool", default=None)
|
|
534
|
+
ov_check.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
|
|
535
|
+
default=None)
|
|
536
|
+
ov_check.add_argument("--path", dest="project_path", default=".",
|
|
537
|
+
help="Project directory (default: current)")
|
|
538
|
+
|
|
539
|
+
ov_sweep = override_subs.add_parser("sweep", help="Drop expired / spent grants now")
|
|
540
|
+
ov_sweep.add_argument("--path", dest="project_path", default=".",
|
|
541
|
+
help="Project directory (default: current)")
|
|
542
|
+
|
|
543
|
+
# Requests: what agents have ASKED for. Deciding is human-only and lives
|
|
544
|
+
# here (and, later, on the phone) — never on the c3_override agent tool.
|
|
545
|
+
ov_requests = override_subs.add_parser(
|
|
546
|
+
"requests", help="Override requests agents have asked for"
|
|
547
|
+
)
|
|
548
|
+
ov_requests.add_argument("--status", default="",
|
|
549
|
+
help="pending | approved | denied | expired | withdrawn")
|
|
550
|
+
ov_requests.add_argument("--all", dest="all_projects", action="store_true",
|
|
551
|
+
help="Every project, not just this one")
|
|
552
|
+
ov_requests.add_argument("--path", dest="project_path", default=".",
|
|
553
|
+
help="Project directory (default: current)")
|
|
554
|
+
|
|
555
|
+
ov_approve = override_subs.add_parser(
|
|
556
|
+
"approve", help="Approve one request — mints a single-use grant"
|
|
557
|
+
)
|
|
558
|
+
ov_approve.add_argument("request_id", help="Request id (ovr_…)")
|
|
559
|
+
ov_approve.add_argument("--ttl", type=int, default=None, dest="ttl_s",
|
|
560
|
+
help="Seconds until the grant expires (clamped to override.max_ttl_s)")
|
|
561
|
+
ov_approve.add_argument("--uses", type=int, default=None,
|
|
562
|
+
help="Uses allowed (default 1)")
|
|
563
|
+
ov_approve.add_argument("--note", default="",
|
|
564
|
+
help="Note recorded with the decision")
|
|
565
|
+
ov_approve.add_argument("--confirm", default=None,
|
|
566
|
+
help="Required for deny/builtin rules: retype the rule glob by hand")
|
|
567
|
+
ov_approve.add_argument("--path", dest="project_path", default=".",
|
|
568
|
+
help="Project directory (default: current)")
|
|
569
|
+
|
|
570
|
+
ov_deny = override_subs.add_parser("deny", help="Refuse one request")
|
|
571
|
+
ov_deny.add_argument("request_id", help="Request id (ovr_…)")
|
|
572
|
+
ov_deny.add_argument("--note", default="",
|
|
573
|
+
help="Note the agent will see with the refusal")
|
|
574
|
+
ov_deny.add_argument("--path", dest="project_path", default=".",
|
|
575
|
+
help="Project directory (default: current)")
|
|
576
|
+
|
|
475
577
|
# ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
|
|
476
578
|
# LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
|
|
477
579
|
# `c3 access` (path policy — a security boundary) and from
|