code-context-control 2.70.0__tar.gz → 2.71.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.70.0/code_context_control.egg-info → code_context_control-2.71.0}/PKG-INFO +1 -1
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/c3.py +1 -1
- {code_context_control-2.70.0 → code_context_control-2.71.0/code_context_control.egg-info}/PKG-INFO +1 -1
- {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/SOURCES.txt +1 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/config.py +9 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/mobile_api.py +469 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/pyproject.toml +1 -1
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_requests.py +195 -2
- code_context_control-2.71.0/tests/test_mobile_override_routes.py +747 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/LICENSE +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/README.md +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/common.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/commands/parser.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/docs.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/edits.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/access.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/index.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_read.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_server.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/mcp_server.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/progress.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/server.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/override.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/project.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/read.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/search.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/session.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/status.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/api.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/app.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/core/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/core/config.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/core/ide.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/core/web_security.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/oracle_server.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/settings.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/access_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/access_telemetry.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/activity_log.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agent_base.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agent_locks.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/agents.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/artifact_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/auto_memory.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/claude_md.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/compressor.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/conversation_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/credential_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/doc_index.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/embedding_index.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/enforcement_policy.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/error_reporting.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/file_memory.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/git_context.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/hub_service.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/indexer.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_client.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/jira_links.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_activation.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/mask_presets.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_graph.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_queue.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/metrics.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/notifications.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_client.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/output_filter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_grants.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/override_policy.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/parser.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/project_manager.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/project_runtime.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/protocol.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/proxy_state.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/repo_map.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/retention.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/router.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/runtime.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/scanner.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_manager.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/session_preloader.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/subprojects.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/task_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/telemetry.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/text_index.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/time_tracker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/transcript_index.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/validation_cache.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/vector_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/watcher.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/setup.cfg +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_override_grants.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_override_requests.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_retention.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_validate.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/backend.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/main.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.70.0 → code_context_control-2.71.0}/tui/theme.tcss +0 -0
{code_context_control-2.70.0/code_context_control.egg-info → code_context_control-2.71.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.71.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
|
{code_context_control-2.70.0 → code_context_control-2.71.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.71.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
|
|
@@ -330,6 +330,7 @@ tests/test_memory_graph_api.py
|
|
|
330
330
|
tests/test_memory_queue.py
|
|
331
331
|
tests/test_memory_system.py
|
|
332
332
|
tests/test_mobile_api.py
|
|
333
|
+
tests/test_mobile_override_routes.py
|
|
333
334
|
tests/test_mobile_security_api.py
|
|
334
335
|
tests/test_no_stale_model_pins.py
|
|
335
336
|
tests/test_notification_dedup.py
|
|
@@ -37,6 +37,15 @@ DEFAULTS = {
|
|
|
37
37
|
# A global-scope access rule applies to EVERY project on this machine —
|
|
38
38
|
# blast radius beyond the projects the token can even enumerate.
|
|
39
39
|
"mobile_access_global_scope": False,
|
|
40
|
+
# ── Override Requests (docs/override-requests.md §3.2) ──
|
|
41
|
+
# The approval inbox. `override` serves the read routes (list / one /
|
|
42
|
+
# policy); `override_write` is what turns a phone tap into a real grant,
|
|
43
|
+
# so it is the switch that matters. Both default ON to match the access
|
|
44
|
+
# pair — the feature is already off by default one layer down, in the
|
|
45
|
+
# PROJECT policy (`override.enabled: false`, every layer false), which is
|
|
46
|
+
# what decides whether a request can exist at all.
|
|
47
|
+
"mobile_override_enabled": True, # read: request list / one / policy
|
|
48
|
+
"mobile_override_write": True, # decide / mute / policy edit
|
|
40
49
|
# Second, tighter budget for security mutations, on top of the shared
|
|
41
50
|
# api_rate_limit_per_min bucket. 0 = share the main bucket only.
|
|
42
51
|
"mobile_security_rate_limit_per_min": 12,
|
|
@@ -66,6 +66,7 @@ CAPABILITIES = [
|
|
|
66
66
|
"credentials", "credentials_write",
|
|
67
67
|
"access", "access_write",
|
|
68
68
|
"enforcement", "enforcement_write",
|
|
69
|
+
"override", "override_write",
|
|
69
70
|
]
|
|
70
71
|
|
|
71
72
|
# capability -> config key that gates it. Absent from this map = always on.
|
|
@@ -76,6 +77,8 @@ _CAPABILITY_SWITCHES = {
|
|
|
76
77
|
"access_write": "mobile_access_write",
|
|
77
78
|
"enforcement": "mobile_access_enabled",
|
|
78
79
|
"enforcement_write": "mobile_access_write",
|
|
80
|
+
"override": "mobile_override_enabled",
|
|
81
|
+
"override_write": "mobile_override_write",
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
FEED_TYPES = ("notification", "activity", "edit", "session_stat")
|
|
@@ -1616,3 +1619,469 @@ def mobile_enforcement_set():
|
|
|
1616
1619
|
pass
|
|
1617
1620
|
_gw_audit("mobile.enforcement.set_mode", {"mode": result["mode"]})
|
|
1618
1621
|
return jsonify(result)
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
# ── Override Requests (docs/override-requests.md §8) ──────
|
|
1625
|
+
# The approval inbox: the agent asks, the phone answers, the answer is a
|
|
1626
|
+
# narrow time-boxed grant rather than a policy change. These routes are
|
|
1627
|
+
# deliberately THIN — every decision, refusal and grant-mint lives in
|
|
1628
|
+
# services/override_requests.py so the CLI (`c3 override`) and this surface
|
|
1629
|
+
# cannot drift apart on the one check that matters (the typed confirmation).
|
|
1630
|
+
#
|
|
1631
|
+
# The wire contract is frozen in §3.3 and re-asserted by
|
|
1632
|
+
# tests/test_mobile_override_routes.py: the request row is serialised through
|
|
1633
|
+
# an explicit allowlist, not `dict(row)`, so an internal field added later
|
|
1634
|
+
# (e.g. `path_key`) cannot silently start crossing the network.
|
|
1635
|
+
|
|
1636
|
+
#: §3.3 field order, verbatim. The mobile client reads these names.
|
|
1637
|
+
_OVERRIDE_FIELDS = (
|
|
1638
|
+
"id", "project_path", "session_id", "created_at", "expires_at", "status",
|
|
1639
|
+
"layer", "rule", "rule_class", "scope", "tool", "op", "path", "refusal",
|
|
1640
|
+
"justification", "resolved_at", "decided_by", "decision_note",
|
|
1641
|
+
)
|
|
1642
|
+
|
|
1643
|
+
#: Decisions made here are always attributed to the phone (§3.3 decided_by).
|
|
1644
|
+
_DECIDED_BY = "mobile"
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
def _override_row(row: dict) -> dict:
|
|
1648
|
+
"""One request row for the wire. Allowlisted, never the raw dict.
|
|
1649
|
+
|
|
1650
|
+
``path_key`` is deliberately absent: it is a canonicalised local
|
|
1651
|
+
filesystem identity used for grant matching, and the phone already gets
|
|
1652
|
+
the human-readable ``path``.
|
|
1653
|
+
"""
|
|
1654
|
+
out = {k: row.get(k) for k in _OVERRIDE_FIELDS}
|
|
1655
|
+
# Decision extras — present only once decided, so a pending card is exactly
|
|
1656
|
+
# the §3.3 shape and nothing more.
|
|
1657
|
+
for extra in ("grant_id", "grant_mode", "muted"):
|
|
1658
|
+
if row.get(extra) is not None:
|
|
1659
|
+
out[extra] = row.get(extra)
|
|
1660
|
+
return out
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
def _override_audit(action: str, row: dict, *, confirmed: bool = False,
|
|
1664
|
+
detail_extra: dict | None = None) -> None:
|
|
1665
|
+
"""Identifiers and rule globs only — never the justification.
|
|
1666
|
+
|
|
1667
|
+
The justification is agent-supplied untrusted text (§3.3); copying it into
|
|
1668
|
+
the activity log would put attacker-controlled prose into a surface the
|
|
1669
|
+
feed ships verbatim to every client.
|
|
1670
|
+
"""
|
|
1671
|
+
detail = {"kind": "override", "action": action,
|
|
1672
|
+
"request_id": row.get("id", ""),
|
|
1673
|
+
"session_id": row.get("session_id", ""),
|
|
1674
|
+
"rule": row.get("rule", ""),
|
|
1675
|
+
"rule_class": row.get("rule_class", ""),
|
|
1676
|
+
"tool": row.get("tool", ""), "op": row.get("op", ""),
|
|
1677
|
+
"via": "oracle-mobile", "caller": _caller(),
|
|
1678
|
+
"confirmed": bool(confirmed)}
|
|
1679
|
+
detail.update(detail_extra or {})
|
|
1680
|
+
project = row.get("project_path") or ""
|
|
1681
|
+
if not project:
|
|
1682
|
+
return
|
|
1683
|
+
try:
|
|
1684
|
+
from services.activity_log import ActivityLog
|
|
1685
|
+
ActivityLog(str(project)).log("access_action", dict(detail))
|
|
1686
|
+
except Exception:
|
|
1687
|
+
pass
|
|
1688
|
+
try:
|
|
1689
|
+
from services.edit_ledger import EditLedger
|
|
1690
|
+
EditLedger(str(project)).log_edit(
|
|
1691
|
+
file=f"override://{row.get('id', '')}",
|
|
1692
|
+
change_type=f"override_{action}",
|
|
1693
|
+
summary=(f"{action} override {row.get('id', '')} "
|
|
1694
|
+
f"({row.get('rule', '')}) via mobile"),
|
|
1695
|
+
tags=["override", action], detail=dict(detail))
|
|
1696
|
+
except Exception:
|
|
1697
|
+
pass
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
def _override_write_gate():
|
|
1701
|
+
"""Capability + rate budget for every override mutation."""
|
|
1702
|
+
gate = _feature_or_404("override") or _feature_or_404("override_write")
|
|
1703
|
+
if gate:
|
|
1704
|
+
return gate
|
|
1705
|
+
return _security_gate()
|
|
1706
|
+
|
|
1707
|
+
|
|
1708
|
+
@bp.route("/overrides", methods=["GET"])
|
|
1709
|
+
def mobile_overrides_list():
|
|
1710
|
+
"""Pending + recently-decided requests, newest first.
|
|
1711
|
+
|
|
1712
|
+
``project`` is OPTIONAL and omitting it means every project (§15.5): the
|
|
1713
|
+
entire point of the inbox is answering while away from the desk, and a
|
|
1714
|
+
phone that must first pick the right project to discover it has a pending
|
|
1715
|
+
approval defeats that.
|
|
1716
|
+
"""
|
|
1717
|
+
gate = _feature_or_404("override")
|
|
1718
|
+
if gate:
|
|
1719
|
+
return gate
|
|
1720
|
+
from services import override_requests as orq
|
|
1721
|
+
raw_project = (request.args.get("project") or "").strip()
|
|
1722
|
+
project_path = ""
|
|
1723
|
+
if raw_project:
|
|
1724
|
+
# A named project must still be one this token can see.
|
|
1725
|
+
resolved, err = _project_or_404(raw_project)
|
|
1726
|
+
if err:
|
|
1727
|
+
return err
|
|
1728
|
+
project_path = str(resolved)
|
|
1729
|
+
status = (request.args.get("status") or "").strip().lower()
|
|
1730
|
+
try:
|
|
1731
|
+
limit = int(request.args.get("limit") or _DEFAULT_LIMIT)
|
|
1732
|
+
except (TypeError, ValueError):
|
|
1733
|
+
limit = _DEFAULT_LIMIT
|
|
1734
|
+
limit = max(1, min(limit, _MAX_LIMIT))
|
|
1735
|
+
try:
|
|
1736
|
+
rows = orq.list_requests(project_path=project_path, status=status,
|
|
1737
|
+
limit=limit)
|
|
1738
|
+
except Exception as exc:
|
|
1739
|
+
return _svc_error(exc)
|
|
1740
|
+
if not raw_project:
|
|
1741
|
+
# Cross-project listing must not leak rows for projects this gateway
|
|
1742
|
+
# does not serve — the store is one file across every project on the
|
|
1743
|
+
# machine, including ones the scanner has since dropped.
|
|
1744
|
+
rows = [r for r in rows
|
|
1745
|
+
if _resolve_registered(str(r.get("project_path") or "")) is not None]
|
|
1746
|
+
return jsonify({
|
|
1747
|
+
"requests": [_override_row(r) for r in rows],
|
|
1748
|
+
"count": len(rows),
|
|
1749
|
+
"project": project_path,
|
|
1750
|
+
"status": status,
|
|
1751
|
+
"limit": limit,
|
|
1752
|
+
})
|
|
1753
|
+
|
|
1754
|
+
|
|
1755
|
+
@bp.route("/overrides/policy", methods=["GET"])
|
|
1756
|
+
def mobile_overrides_policy_get():
|
|
1757
|
+
"""The effective (already merged + clamped) `override` section (§3.1).
|
|
1758
|
+
|
|
1759
|
+
Registered BEFORE `/overrides/<id>` would otherwise catch it — Flask
|
|
1760
|
+
matches static rules ahead of converters, but the ordering is made
|
|
1761
|
+
explicit here so a later refactor cannot turn `policy` into a request id.
|
|
1762
|
+
"""
|
|
1763
|
+
gate = _feature_or_404("override")
|
|
1764
|
+
if gate:
|
|
1765
|
+
return gate
|
|
1766
|
+
from services import override_policy as opol
|
|
1767
|
+
resolved, err = _project_or_404(request.args.get("project") or "")
|
|
1768
|
+
if err:
|
|
1769
|
+
return err
|
|
1770
|
+
policy = opol.resolve(str(resolved))
|
|
1771
|
+
return jsonify({
|
|
1772
|
+
"path": str(resolved),
|
|
1773
|
+
"policy": policy.as_dict(),
|
|
1774
|
+
"layers": list(opol.LAYER_KEYS),
|
|
1775
|
+
"typed_confirm_layers": sorted(opol.TYPED_CONFIRM_LAYERS),
|
|
1776
|
+
"hard_max_ttl_s": opol.HARD_MAX_TTL_S,
|
|
1777
|
+
"defaults": {"max_ttl_s": opol.DEFAULTS["max_ttl_s"],
|
|
1778
|
+
"default_uses": opol.DEFAULTS["default_uses"]},
|
|
1779
|
+
"writable": bool(_cfg().get("mobile_override_write", True)),
|
|
1780
|
+
# Verbatim from the spec: policy is a floor, not a promise. Global and
|
|
1781
|
+
# project merge by TIGHTENING, so a layer shown true here can still be
|
|
1782
|
+
# false in effect if the other scope forbids it.
|
|
1783
|
+
"coverage_note": (
|
|
1784
|
+
"Project and global policy merge by tightening only: a layer is "
|
|
1785
|
+
"escalatable iff both scopes allow it, and the numeric limits take "
|
|
1786
|
+
"the smaller value. Grants are honoured on the PreToolUse hook "
|
|
1787
|
+
"surface; the c3_* MCP tools do not consult them yet (P2a)."
|
|
1788
|
+
),
|
|
1789
|
+
})
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
@bp.route("/overrides/policy", methods=["POST"])
|
|
1793
|
+
def mobile_overrides_policy_set():
|
|
1794
|
+
"""Edit the project's `override` section.
|
|
1795
|
+
|
|
1796
|
+
Widening — enabling the feature, turning a layer on, raising a ceiling, or
|
|
1797
|
+
allowing session grants — needs a typed confirmation. Tightening never
|
|
1798
|
+
does: making the guard stricter is always allowed to be one tap.
|
|
1799
|
+
"""
|
|
1800
|
+
gate = _override_write_gate()
|
|
1801
|
+
if gate:
|
|
1802
|
+
return gate
|
|
1803
|
+
from services import override_policy as opol
|
|
1804
|
+
data = request.get_json(silent=True) or {}
|
|
1805
|
+
resolved, err = _project_or_404(data.get("project") or "")
|
|
1806
|
+
if err:
|
|
1807
|
+
return err
|
|
1808
|
+
|
|
1809
|
+
section = data.get("override")
|
|
1810
|
+
if not isinstance(section, dict):
|
|
1811
|
+
return jsonify({"error": "body needs an 'override' object"}), 400
|
|
1812
|
+
unknown = sorted(set(section) - set(opol.DEFAULTS))
|
|
1813
|
+
if unknown:
|
|
1814
|
+
# §3.1: unknown keys are a hard error, never a silent no-op.
|
|
1815
|
+
return jsonify({
|
|
1816
|
+
"error": f"unknown override key(s): {', '.join(unknown)}",
|
|
1817
|
+
"valid": sorted(opol.DEFAULTS),
|
|
1818
|
+
}), 400
|
|
1819
|
+
layers = section.get("layers")
|
|
1820
|
+
if layers is not None:
|
|
1821
|
+
if not isinstance(layers, dict):
|
|
1822
|
+
return jsonify({"error": "'layers' must be an object"}), 400
|
|
1823
|
+
bad = sorted(set(layers) - set(opol.LAYER_KEYS))
|
|
1824
|
+
if bad:
|
|
1825
|
+
return jsonify({"error": f"unknown layer(s): {', '.join(bad)}",
|
|
1826
|
+
"valid": list(opol.LAYER_KEYS)}), 400
|
|
1827
|
+
|
|
1828
|
+
current = opol.resolve(str(resolved))
|
|
1829
|
+
widened = _override_widenings(current, section)
|
|
1830
|
+
if widened and not _confirmed(data, "widen"):
|
|
1831
|
+
return jsonify({
|
|
1832
|
+
"error": "this widens what a phone tap can approve: "
|
|
1833
|
+
+ ", ".join(widened),
|
|
1834
|
+
"widens": widened,
|
|
1835
|
+
"needs_confirmation": True, "confirm_with": "widen",
|
|
1836
|
+
}), 400
|
|
1837
|
+
|
|
1838
|
+
try:
|
|
1839
|
+
result = _write_override_section(resolved, section)
|
|
1840
|
+
except (OSError, ValueError) as exc:
|
|
1841
|
+
return _svc_error(exc)
|
|
1842
|
+
|
|
1843
|
+
_override_audit("policy_set",
|
|
1844
|
+
{"id": "", "project_path": str(resolved), "rule": ""},
|
|
1845
|
+
confirmed=bool(widened),
|
|
1846
|
+
detail_extra={"widens": widened,
|
|
1847
|
+
"keys": sorted(section)})
|
|
1848
|
+
_gw_audit("mobile.override.policy_set",
|
|
1849
|
+
{"keys": sorted(section), "widens": widened})
|
|
1850
|
+
effective = opol.resolve(str(resolved))
|
|
1851
|
+
return jsonify({"path": str(resolved), "written": result,
|
|
1852
|
+
"policy": effective.as_dict(), "widened": widened})
|
|
1853
|
+
|
|
1854
|
+
|
|
1855
|
+
def _override_widenings(current, section: dict) -> list:
|
|
1856
|
+
"""Which requested changes LOOSEN the policy. Names, for the challenge."""
|
|
1857
|
+
out = []
|
|
1858
|
+
if section.get("enabled") and not current.enabled:
|
|
1859
|
+
out.append("enabled")
|
|
1860
|
+
for key, want in (section.get("layers") or {}).items():
|
|
1861
|
+
if want and not current.layers.get(key, False):
|
|
1862
|
+
out.append(f"layers.{key}")
|
|
1863
|
+
for key in ("max_ttl_s", "default_uses", "request_ttl_s",
|
|
1864
|
+
"max_pending_per_session", "max_requests_per_hour"):
|
|
1865
|
+
if key in section:
|
|
1866
|
+
try:
|
|
1867
|
+
if int(section[key]) > int(getattr(current, key)):
|
|
1868
|
+
out.append(key)
|
|
1869
|
+
except (TypeError, ValueError):
|
|
1870
|
+
pass
|
|
1871
|
+
if section.get("allow_session_grants") and not current.allow_session_grants:
|
|
1872
|
+
out.append("allow_session_grants")
|
|
1873
|
+
return out
|
|
1874
|
+
|
|
1875
|
+
|
|
1876
|
+
def _write_override_section(project: Path, section: dict) -> dict:
|
|
1877
|
+
"""Merge *section* into the project's `.c3/config.json` `override` block.
|
|
1878
|
+
|
|
1879
|
+
Project scope only. A global override policy governs every project on the
|
|
1880
|
+
machine, and the phone has no affordance for reviewing that blast radius —
|
|
1881
|
+
same call `mobile_enforcement_set` makes about machine-wide discipline.
|
|
1882
|
+
"""
|
|
1883
|
+
cfg_file = Path(project) / ".c3" / "config.json"
|
|
1884
|
+
cfg_file.parent.mkdir(parents=True, exist_ok=True)
|
|
1885
|
+
cfg = {}
|
|
1886
|
+
if cfg_file.is_file():
|
|
1887
|
+
try:
|
|
1888
|
+
cfg = json.loads(cfg_file.read_text(encoding="utf-8")) or {}
|
|
1889
|
+
except ValueError as exc:
|
|
1890
|
+
raise ValueError(f"project config is not valid JSON: {exc}") from exc
|
|
1891
|
+
if not isinstance(cfg, dict):
|
|
1892
|
+
raise ValueError("project config is not a JSON object")
|
|
1893
|
+
block = dict(cfg.get("override") or {})
|
|
1894
|
+
for key, value in section.items():
|
|
1895
|
+
if key == "layers":
|
|
1896
|
+
merged = dict(block.get("layers") or {})
|
|
1897
|
+
merged.update({k: bool(v) for k, v in (value or {}).items()})
|
|
1898
|
+
block["layers"] = merged
|
|
1899
|
+
else:
|
|
1900
|
+
block[key] = value
|
|
1901
|
+
cfg["override"] = block
|
|
1902
|
+
tmp = cfg_file.with_name(f"{cfg_file.name}.tmp")
|
|
1903
|
+
tmp.write_text(json.dumps(cfg, indent=2, ensure_ascii=False),
|
|
1904
|
+
encoding="utf-8")
|
|
1905
|
+
tmp.replace(cfg_file)
|
|
1906
|
+
return block
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
@bp.route("/overrides/<request_id>", methods=["GET"])
|
|
1910
|
+
def mobile_override_get(request_id):
|
|
1911
|
+
"""One request, full context."""
|
|
1912
|
+
gate = _feature_or_404("override")
|
|
1913
|
+
if gate:
|
|
1914
|
+
return gate
|
|
1915
|
+
from services import override_requests as orq
|
|
1916
|
+
row = orq.get(str(request_id))
|
|
1917
|
+
if row is None:
|
|
1918
|
+
return jsonify({"error": "unknown request"}), 404
|
|
1919
|
+
if _resolve_registered(str(row.get("project_path") or "")) is None:
|
|
1920
|
+
# Same refusal as an unknown id: whether a request exists for a project
|
|
1921
|
+
# this gateway does not serve is not this token's business.
|
|
1922
|
+
return jsonify({"error": "unknown request"}), 404
|
|
1923
|
+
from services import override_policy as opol
|
|
1924
|
+
policy = opol.resolve(str(row.get("project_path") or "."))
|
|
1925
|
+
return jsonify({
|
|
1926
|
+
"request": _override_row(row),
|
|
1927
|
+
# What approving would actually cost, so the card can say it BEFORE
|
|
1928
|
+
# the tap rather than reporting a clamp afterwards.
|
|
1929
|
+
"policy": {
|
|
1930
|
+
"max_ttl_s": policy.max_ttl_s,
|
|
1931
|
+
"default_uses": policy.default_uses,
|
|
1932
|
+
"allow_session_grants": policy.allow_session_grants,
|
|
1933
|
+
"escalatable": policy.escalatable(str(row.get("rule_class") or "")),
|
|
1934
|
+
},
|
|
1935
|
+
"needs_typed_confirm": (
|
|
1936
|
+
str(row.get("rule_class") or "") in opol.TYPED_CONFIRM_LAYERS),
|
|
1937
|
+
"confirm_with": (
|
|
1938
|
+
row.get("rule") if str(row.get("rule_class") or "")
|
|
1939
|
+
in opol.TYPED_CONFIRM_LAYERS else ""),
|
|
1940
|
+
})
|
|
1941
|
+
|
|
1942
|
+
|
|
1943
|
+
@bp.route("/overrides/<request_id>/decide", methods=["POST"])
|
|
1944
|
+
def mobile_override_decide(request_id):
|
|
1945
|
+
"""Approve (minting a grant) or deny one request.
|
|
1946
|
+
|
|
1947
|
+
The typed-confirmation challenges are computed here for the CLIENT's
|
|
1948
|
+
benefit — the authoritative check is inside ``override_requests.decide``,
|
|
1949
|
+
which refuses regardless of what this route believes. Two places compute
|
|
1950
|
+
it; only one enforces it.
|
|
1951
|
+
"""
|
|
1952
|
+
gate = _override_write_gate()
|
|
1953
|
+
if gate:
|
|
1954
|
+
return gate
|
|
1955
|
+
from services import override_policy as opol
|
|
1956
|
+
from services import override_requests as orq
|
|
1957
|
+
data = request.get_json(silent=True) or {}
|
|
1958
|
+
decision = str(data.get("decision") or "").strip().lower()
|
|
1959
|
+
if decision not in (orq.DECISION_APPROVE, orq.DECISION_DENY):
|
|
1960
|
+
return jsonify({"error": "decision must be 'approve' or 'deny'"}), 400
|
|
1961
|
+
mode = str(data.get("mode") or orq.MODE_ONCE).strip().lower()
|
|
1962
|
+
if mode not in orq.MODES:
|
|
1963
|
+
return jsonify({"error": f"mode must be one of: "
|
|
1964
|
+
f"{', '.join(orq.MODES)}"}), 400
|
|
1965
|
+
|
|
1966
|
+
row = orq.get(str(request_id))
|
|
1967
|
+
if row is None or _resolve_registered(str(row.get("project_path") or "")) is None:
|
|
1968
|
+
return jsonify({"error": "unknown request"}), 404
|
|
1969
|
+
if row.get("status") != orq.STATUS_PENDING:
|
|
1970
|
+
# §12.7: it lapsed while the phone was showing it. 409, and the card
|
|
1971
|
+
# refreshes to the real status instead of silently minting a grant.
|
|
1972
|
+
return jsonify({"error": f"request is {row.get('status')}, not pending",
|
|
1973
|
+
"request": _override_row(row)}), 409
|
|
1974
|
+
|
|
1975
|
+
project_path = str(row.get("project_path") or ".")
|
|
1976
|
+
policy = opol.resolve(project_path)
|
|
1977
|
+
rule_class = str(row.get("rule_class") or "")
|
|
1978
|
+
confirm = data.get("confirm")
|
|
1979
|
+
|
|
1980
|
+
if decision == orq.DECISION_APPROVE:
|
|
1981
|
+
# (a) approving an access_deny / access_builtin request — the challenge
|
|
1982
|
+
# is the RULE GLOB itself, typed by hand (§8, §11 threat 1).
|
|
1983
|
+
if rule_class in opol.TYPED_CONFIRM_LAYERS and confirm != row.get("rule"):
|
|
1984
|
+
return jsonify({
|
|
1985
|
+
"error": f"approving an {rule_class} request needs the rule "
|
|
1986
|
+
"glob retyped by hand",
|
|
1987
|
+
"needs_confirmation": True,
|
|
1988
|
+
"confirm_with": row.get("rule"),
|
|
1989
|
+
}), 400
|
|
1990
|
+
# (b) a session grant, and only where policy allows one at all.
|
|
1991
|
+
if mode == orq.MODE_SESSION:
|
|
1992
|
+
if not policy.allow_session_grants:
|
|
1993
|
+
return jsonify({
|
|
1994
|
+
"error": "session grants are disabled for this project",
|
|
1995
|
+
"detail": "`override.allow_session_grants` is false — "
|
|
1996
|
+
"approve once instead.",
|
|
1997
|
+
}), 403
|
|
1998
|
+
if (rule_class not in opol.TYPED_CONFIRM_LAYERS
|
|
1999
|
+
and confirm != orq.CONFIRM_SESSION):
|
|
2000
|
+
return jsonify({
|
|
2001
|
+
"error": "a session grant is unlimited-use until it expires",
|
|
2002
|
+
"needs_confirmation": True,
|
|
2003
|
+
"confirm_with": orq.CONFIRM_SESSION,
|
|
2004
|
+
}), 400
|
|
2005
|
+
|
|
2006
|
+
requested_ttl = data.get("ttl_s")
|
|
2007
|
+
try:
|
|
2008
|
+
requested_ttl = int(requested_ttl) if requested_ttl is not None else None
|
|
2009
|
+
except (TypeError, ValueError):
|
|
2010
|
+
return jsonify({"error": "ttl_s must be an integer"}), 400
|
|
2011
|
+
requested_uses = data.get("uses")
|
|
2012
|
+
try:
|
|
2013
|
+
requested_uses = int(requested_uses) if requested_uses is not None else None
|
|
2014
|
+
except (TypeError, ValueError):
|
|
2015
|
+
return jsonify({"error": "uses must be an integer"}), 400
|
|
2016
|
+
|
|
2017
|
+
try:
|
|
2018
|
+
result = orq.decide(
|
|
2019
|
+
str(request_id), decision, uses=requested_uses,
|
|
2020
|
+
ttl_s=requested_ttl, note=str(data.get("note") or ""),
|
|
2021
|
+
decided_by=_DECIDED_BY, confirm=confirm, mode=mode,
|
|
2022
|
+
mute=bool(data.get("mute")))
|
|
2023
|
+
except orq.OverrideError as exc:
|
|
2024
|
+
_override_audit(f"{decision}_refused", row)
|
|
2025
|
+
return jsonify({"error": str(exc)}), 400
|
|
2026
|
+
except Exception as exc:
|
|
2027
|
+
return _svc_error(exc)
|
|
2028
|
+
|
|
2029
|
+
_override_audit(decision, result,
|
|
2030
|
+
confirmed=rule_class in opol.TYPED_CONFIRM_LAYERS,
|
|
2031
|
+
detail_extra={"mode": mode,
|
|
2032
|
+
"grant_id": result.get("grant_id", "")})
|
|
2033
|
+
_gw_audit(f"mobile.override.{decision}",
|
|
2034
|
+
{"request_id": str(request_id), "rule_class": rule_class,
|
|
2035
|
+
"mode": mode})
|
|
2036
|
+
|
|
2037
|
+
payload = {"request": _override_row(result), "decision": decision}
|
|
2038
|
+
if decision == orq.DECISION_APPROVE:
|
|
2039
|
+
granted_ttl = policy.clamp_ttl(requested_ttl)
|
|
2040
|
+
payload["grant"] = {
|
|
2041
|
+
"id": result.get("grant_id", ""),
|
|
2042
|
+
"mode": result.get("grant_mode", mode),
|
|
2043
|
+
"ttl_s": granted_ttl,
|
|
2044
|
+
"uses": policy.clamp_uses(
|
|
2045
|
+
opol.HARD_MAX_USES if mode == orq.MODE_SESSION
|
|
2046
|
+
else requested_uses),
|
|
2047
|
+
}
|
|
2048
|
+
# §8: a client asking for a week gets 15 minutes and is TOLD so.
|
|
2049
|
+
if requested_ttl is not None and granted_ttl < requested_ttl:
|
|
2050
|
+
payload["clamped"] = True
|
|
2051
|
+
payload["clamped_note"] = (
|
|
2052
|
+
f"requested ttl_s={requested_ttl} was clamped to "
|
|
2053
|
+
f"{granted_ttl}s by this project's override.max_ttl_s "
|
|
2054
|
+
f"({policy.max_ttl_s}s).")
|
|
2055
|
+
return jsonify(payload)
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
@bp.route("/overrides/<request_id>/mute", methods=["POST"])
|
|
2059
|
+
def mobile_override_mute(request_id):
|
|
2060
|
+
"""Deny, and suppress identical requests for this session (§8).
|
|
2061
|
+
|
|
2062
|
+
Never needs a confirmation: muting only ever makes the agent MORE blocked,
|
|
2063
|
+
and deny must always be the cheaper gesture than approve (§9).
|
|
2064
|
+
"""
|
|
2065
|
+
gate = _override_write_gate()
|
|
2066
|
+
if gate:
|
|
2067
|
+
return gate
|
|
2068
|
+
from services import override_requests as orq
|
|
2069
|
+
data = request.get_json(silent=True) or {}
|
|
2070
|
+
row = orq.get(str(request_id))
|
|
2071
|
+
if row is None or _resolve_registered(str(row.get("project_path") or "")) is None:
|
|
2072
|
+
return jsonify({"error": "unknown request"}), 404
|
|
2073
|
+
if row.get("status") != orq.STATUS_PENDING:
|
|
2074
|
+
return jsonify({"error": f"request is {row.get('status')}, not pending",
|
|
2075
|
+
"request": _override_row(row)}), 409
|
|
2076
|
+
try:
|
|
2077
|
+
result = orq.decide(str(request_id), orq.DECISION_DENY,
|
|
2078
|
+
note=str(data.get("note") or ""),
|
|
2079
|
+
decided_by=_DECIDED_BY, mute=True)
|
|
2080
|
+
except orq.OverrideError as exc:
|
|
2081
|
+
return jsonify({"error": str(exc)}), 400
|
|
2082
|
+
except Exception as exc:
|
|
2083
|
+
return _svc_error(exc)
|
|
2084
|
+
_override_audit("mute", result, detail_extra={"muted": True})
|
|
2085
|
+
_gw_audit("mobile.override.mute", {"request_id": str(request_id)})
|
|
2086
|
+
return jsonify({"request": _override_row(result), "decision": "deny",
|
|
2087
|
+
"muted": True})
|
|
@@ -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.71.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"
|