code-context-control 2.126.1__tar.gz → 2.127.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.126.1 → code_context_control-2.127.0}/PKG-INFO +1 -1
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/c3.py +37 -2
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/commands/parser.py +12 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_server.py +37 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_access.js +111 -11
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/PKG-INFO +1 -1
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/SOURCES.txt +3 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/mobile_api.py +72 -1
- {code_context_control-2.126.1 → code_context_control-2.127.0}/pyproject.toml +1 -1
- code_context_control-2.127.0/services/override_costs.py +183 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_override_routes.py +73 -0
- code_context_control-2.127.0/tests/test_mobile_override_costs.py +252 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_override_routes.py +49 -0
- code_context_control-2.127.0/tests/test_override_costs.py +320 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/LICENSE +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/README.md +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/_shell_writes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/commands/common.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/docs.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/edits.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/access.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/index.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_codex_lifecycle.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_read.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_session_end.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_session_open.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_tokens.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/mcp_server.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/progress.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/server.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/_grants.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/ci.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/override.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/project.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/read.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/search.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/session.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/shell_job.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/shell_nudge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/shell_parsers.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/shell_render.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/status.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/api.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/app.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/codex_patch.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/config.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/host.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/ide.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/core/web_security.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/config.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/listeners.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/oracle_server.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/chat_poll.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/client_tokens.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/settings.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/access_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/access_telemetry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/activity_log.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/agent_base.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/agent_locks.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/agents.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/artifact_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/atomic_json.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/auto_memory.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/background_service.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/map_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/search_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bench/shell_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_act.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_cache.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_expr.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_failures.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_github.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_impact.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_intel.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_runner.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ci_workflow.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/claude_md.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/codex_integration.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/codex_transcript.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/compressor.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/conversation_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/cred_audit.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/cred_telemetry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/credential_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/dir_map.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/doc_index.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/embedding_index.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/enforcement_policy.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/error_reporting.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/file_map.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/file_memory.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/git_context.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/host_sessions.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/hub_service.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/index_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/indexer.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/jira_client.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/jira_links.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/lexical_index.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/mask_activation.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/mask_presets.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_graph.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_queue.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/metrics.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/notifications.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ollama_client.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/oracle_service.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/output_filter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/override_grants.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/override_policy.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/override_requests.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/override_wake.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/parser.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/project_manager.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/project_runtime.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/protocol.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/proxy_state.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/repo_map.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/repo_shape.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/reranker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/retention.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/retrieval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/router.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/runtime.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/scanner.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/session_manager.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/session_preloader.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/shell_jobs.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/shell_output.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/subprojects.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/task_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/telemetry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/text_index.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/time_tracker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/transcript_index.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/validation_cache.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/vector_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/watcher.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/setup.cfg +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_atomic_json.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_background_service.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_builtin_modes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_act.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_cache.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_expr.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_impact.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_intel.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_claude_md_updater.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_codex_mcp_transport.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_codex_native.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_confirm_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_confirm_guard_wiring.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_cooccurrence_bounds.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_cred_audit.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_cred_telemetry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_credential_login_kind.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_credential_ui_parity.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_dir_map_c3.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_env_parser.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_file_map_c1.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_codex_compat.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_session_end.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_oracle_service_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_index_cap_not_hardcoded.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_init_clear_preserves_user_content.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_large_files_c4.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_map_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_map_telemetry_c0.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_clients.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_extras.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_jobs_ci.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_notification_kinds.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_health_probe.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_loopback.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_override_grants.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_override_requests.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_override_wake.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_repo_shape.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_retention.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p1.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p2.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p2b.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p3.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p4.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_search_p5.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_budget_s1.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_eval.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_jobs_s3.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_nudge_s4.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_output.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_parsers_s2.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_telemetry_s0.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_write_ledger.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_shell_writes.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subproject_clear_scope.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subproject_depth_consumers.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subproject_linkage_api.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_token_tracking.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_ui_jsx_syntax.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_validate.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/backend.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/main.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.126.1 → code_context_control-2.127.0}/tui/theme.tcss +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.127.0
|
|
4
4
|
Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
|
|
|
92
92
|
# Config
|
|
93
93
|
CONFIG_DIR = ".c3"
|
|
94
94
|
CONFIG_FILE = ".c3/config.json"
|
|
95
|
-
__version__ = "2.
|
|
95
|
+
__version__ = "2.127.0"
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def _compress_file_cli(compressor, path, mode="smart", **kw):
|
|
@@ -6968,8 +6968,10 @@ def cmd_override(args):
|
|
|
6968
6968
|
"""
|
|
6969
6969
|
sub = getattr(args, "override_cmd", None)
|
|
6970
6970
|
if not sub:
|
|
6971
|
-
print("Usage: c3 override <policy|grant|list|revoke|check|sweep
|
|
6971
|
+
print("Usage: c3 override <policy|grant|list|revoke|check|sweep|"
|
|
6972
|
+
"requests|approve|deny|costs> ...")
|
|
6972
6973
|
print(" c3 override policy # what is escalatable right now")
|
|
6974
|
+
print(" c3 override costs # which rules keep holding the agent")
|
|
6973
6975
|
return
|
|
6974
6976
|
|
|
6975
6977
|
from services import override_grants as og
|
|
@@ -6998,6 +7000,39 @@ def cmd_override(args):
|
|
|
6998
7000
|
_override_requests(args, project_path)
|
|
6999
7001
|
elif sub in ("approve", "deny"):
|
|
7000
7002
|
_override_decide(args, sub, project_path)
|
|
7003
|
+
elif sub == "costs":
|
|
7004
|
+
_override_costs(args, project_path)
|
|
7005
|
+
|
|
7006
|
+
|
|
7007
|
+
def _override_costs(args, project_path: str) -> None:
|
|
7008
|
+
"""`c3 override costs` — which rules keep holding the agent (§11 T5)."""
|
|
7009
|
+
from services import override_costs as ocost
|
|
7010
|
+
|
|
7011
|
+
days = max(1, int(getattr(args, "days", None) or ocost.DEFAULT_DAYS))
|
|
7012
|
+
everywhere = bool(getattr(args, "all_projects", False))
|
|
7013
|
+
rows = ocost.rule_costs(project_path=None if everywhere else project_path,
|
|
7014
|
+
days=days)
|
|
7015
|
+
print_header(f"Override costs — last {days} day(s)"
|
|
7016
|
+
+ ("" if everywhere else f" · {Path(project_path).name}"))
|
|
7017
|
+
if not rows:
|
|
7018
|
+
print(" (no requests in the window)")
|
|
7019
|
+
return
|
|
7020
|
+
width = max(len(r["rule"]) for r in rows)
|
|
7021
|
+
print(f" {'held':>4} {'ok':>3} {'no':>3} {'exp':>3} {'wait':>4} "
|
|
7022
|
+
f"{'rule':<{width}} suggestion")
|
|
7023
|
+
for r in rows:
|
|
7024
|
+
line = (f" {r['count']:>4} {r['approved']:>3} {r['denied']:>3} "
|
|
7025
|
+
f"{r['expired']:>3} {r['pending']:>4} {r['rule']:<{width}} "
|
|
7026
|
+
f"{r['suggestion']}")
|
|
7027
|
+
if r["count"] >= ocost.NUDGE_THRESHOLD:
|
|
7028
|
+
line += " <- costing you"
|
|
7029
|
+
print(line)
|
|
7030
|
+
if everywhere:
|
|
7031
|
+
print(f" {'':22s} {r['project_path']}")
|
|
7032
|
+
print("\n held = requests filed; ok/no/exp/wait = approved/denied/"
|
|
7033
|
+
"expired/still pending (withdrawn counts in held only).")
|
|
7034
|
+
print(" A rule you keep approving is friction: edit it with `c3 access`. "
|
|
7035
|
+
"A rule you keep denying is working — mute the session instead.")
|
|
7001
7036
|
|
|
7002
7037
|
|
|
7003
7038
|
def _override_requests(args, project_path: str) -> None:
|
|
@@ -779,6 +779,18 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
779
779
|
ov_deny.add_argument("--path", dest="project_path", default=".",
|
|
780
780
|
help="Project directory (default: current)")
|
|
781
781
|
|
|
782
|
+
# Costs: which rules keep asking (spec §11 Threat 5, v2.127.0). A read
|
|
783
|
+
# over the request store — it decides nothing and edits no rule.
|
|
784
|
+
ov_costs = override_subs.add_parser(
|
|
785
|
+
"costs", help="Which rules held the agent most, and how you answered"
|
|
786
|
+
)
|
|
787
|
+
ov_costs.add_argument("--days", type=int, default=7,
|
|
788
|
+
help="Trailing window in days (default 7)")
|
|
789
|
+
ov_costs.add_argument("--all", dest="all_projects", action="store_true",
|
|
790
|
+
help="Every project, not just this one")
|
|
791
|
+
ov_costs.add_argument("--path", dest="project_path", default=".",
|
|
792
|
+
help="Project directory (default: current)")
|
|
793
|
+
|
|
782
794
|
# ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
|
|
783
795
|
# LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
|
|
784
796
|
# `c3 access` (path policy — a security boundary) and from
|
|
@@ -3215,6 +3215,43 @@ def api_hub_overrides():
|
|
|
3215
3215
|
"limit": limit})
|
|
3216
3216
|
|
|
3217
3217
|
|
|
3218
|
+
@app.route("/api/hub/overrides/costs", methods=["GET"])
|
|
3219
|
+
def api_hub_override_costs():
|
|
3220
|
+
"""Per-(project, rule) request counts over a trailing window (§11 T5).
|
|
3221
|
+
|
|
3222
|
+
The "this rule is costing you" strip. Query: path (optional — omit for
|
|
3223
|
+
every project in the store; the hub is this machine's own surface and
|
|
3224
|
+
does not filter to a registry), days (1..30, default 7). Response:
|
|
3225
|
+
{days, threshold, rules: [...], count, project} with the row shape of
|
|
3226
|
+
``services.override_costs.FIELDS``; ``threshold`` is the count at which
|
|
3227
|
+
the UI says something, so the strip and the desktop tray agree on it.
|
|
3228
|
+
Registered before the POST-only ``<request_id>`` route so a GET on
|
|
3229
|
+
``costs`` can never fall through to a 405 there.
|
|
3230
|
+
"""
|
|
3231
|
+
from services import override_costs as ocost
|
|
3232
|
+
project_path = ""
|
|
3233
|
+
raw_project = (request.args.get("path") or "").strip()
|
|
3234
|
+
if raw_project:
|
|
3235
|
+
try:
|
|
3236
|
+
project_path = str(_resolve_project_path(raw_project))
|
|
3237
|
+
except ValueError as e:
|
|
3238
|
+
return jsonify({"error": str(e)}), 404
|
|
3239
|
+
try:
|
|
3240
|
+
days = int(request.args.get("days") or ocost.DEFAULT_DAYS)
|
|
3241
|
+
except (TypeError, ValueError):
|
|
3242
|
+
days = ocost.DEFAULT_DAYS
|
|
3243
|
+
days = max(1, min(ocost.MAX_DAYS, days))
|
|
3244
|
+
try:
|
|
3245
|
+
rules = ocost.rule_costs(project_path=project_path or None, days=days)
|
|
3246
|
+
except Exception as exc:
|
|
3247
|
+
return jsonify({"error": str(exc)}), 500
|
|
3248
|
+
for r in rules:
|
|
3249
|
+
r["project_name"] = Path(str(r.get("project_path") or ".")).name
|
|
3250
|
+
return jsonify({"days": days, "threshold": ocost.NUDGE_THRESHOLD,
|
|
3251
|
+
"rules": rules, "count": len(rules),
|
|
3252
|
+
"project": project_path})
|
|
3253
|
+
|
|
3254
|
+
|
|
3218
3255
|
@app.route("/api/hub/overrides/<request_id>", methods=["POST"])
|
|
3219
3256
|
def api_hub_override_decide(request_id):
|
|
3220
3257
|
"""Approve (minting a grant) or deny one request. decided_by='desktop'.
|
{code_context_control-2.126.1 → code_context_control-2.127.0}/cli/hub_ui/components/hub_access.js
RENAMED
|
@@ -185,11 +185,79 @@ function AccRequestCard({ row, busy, onDecide, onTypedApprove, onRuleApprove })
|
|
|
185
185
|
);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
// ── "Costing you" — the §11 Threat 5 nudge ─────────────────────────────────
|
|
189
|
+
// One row per rule that held at least `threshold` times in the window. The
|
|
190
|
+
// count is the argument: a rule the user keeps approving is friction, and a
|
|
191
|
+
// rule they keep denying is doing its job against an agent that will not
|
|
192
|
+
// stop asking. Either way the fix is the RULE, not the next card — so the
|
|
193
|
+
// only affordance is "open the rule", which jumps to it in the policy matrix
|
|
194
|
+
// below. The strip never approves anything and never edits policy; it says
|
|
195
|
+
// the number out loud, which is the mitigation the spec asks for.
|
|
196
|
+
function accSamePath(a, b) {
|
|
197
|
+
const norm = (p) => String(p || '').replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase();
|
|
198
|
+
return norm(a) === norm(b);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function AccCostStrip({ onOpenRule }) {
|
|
202
|
+
const [data, setData] = useState(null);
|
|
203
|
+
|
|
204
|
+
const load = useCallback(async () => {
|
|
205
|
+
try {
|
|
206
|
+
setData(await api.get('/api/hub/overrides/costs?days=7'));
|
|
207
|
+
} catch { /* keep the last good strip */ }
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
useEffect(() => { load(); }, [load]);
|
|
211
|
+
usePoll(load, 15000);
|
|
212
|
+
|
|
213
|
+
if (!data) return null;
|
|
214
|
+
const threshold = Number(data.threshold) || 3;
|
|
215
|
+
const rows = (data.rules || []).filter(r => (Number(r.count) || 0) >= threshold);
|
|
216
|
+
if (rows.length === 0) return null;
|
|
217
|
+
return (
|
|
218
|
+
<div style={{
|
|
219
|
+
background: T.surface, border: `1px solid ${T.warn}`, borderRadius: 10,
|
|
220
|
+
padding: '10px 14px',
|
|
221
|
+
}}>
|
|
222
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 4 }}>
|
|
223
|
+
<span style={{ fontSize: 12.5, fontWeight: 700, color: T.warn }}>Costing you</span>
|
|
224
|
+
<span style={{ fontSize: 11, color: T.textDim }}>
|
|
225
|
+
rules that held {threshold}+ times in the last {data.days} days — fix the
|
|
226
|
+
rule, not the next card
|
|
227
|
+
</span>
|
|
228
|
+
</div>
|
|
229
|
+
{rows.map(r => (
|
|
230
|
+
<div key={`${r.project_path}|${r.rule}`} style={{
|
|
231
|
+
display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap',
|
|
232
|
+
padding: '4px 0', fontSize: 11.5, color: T.textMuted,
|
|
233
|
+
}}>
|
|
234
|
+
<span className="mono" title={r.project_path} style={{
|
|
235
|
+
fontSize: 10.5, padding: '2px 7px', borderRadius: 4,
|
|
236
|
+
background: T.surfaceAlt, color: T.textMuted,
|
|
237
|
+
}}>{r.project_name || r.project_path}</span>
|
|
238
|
+
<span>
|
|
239
|
+
<b className="mono" style={{ color: T.text }}>{r.rule}</b>
|
|
240
|
+
{' '}held {r.count}× this week ({r.approved} approved) — {r.suggestion}
|
|
241
|
+
</span>
|
|
242
|
+
<div style={{ flex: 1 }} />
|
|
243
|
+
<Btn variant="ghost" onClick={() => onOpenRule(r)}
|
|
244
|
+
style={{ padding: '4px 10px' }}>Open rule</Btn>
|
|
245
|
+
</div>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
188
251
|
// ── Read-only per-project policy matrix ────────────────────────────────────
|
|
189
|
-
|
|
252
|
+
// `focus` ({path, rule, seq}) comes from the cost strip's "Open rule": the
|
|
253
|
+
// panel switches to that project, scrolls itself into view and outlines the
|
|
254
|
+
// chip whose glob is the rule. A synthetic rule (discipline / shell) has no
|
|
255
|
+
// chip, so the scroll alone is the answer there.
|
|
256
|
+
function AccRulesPanel({ projects, focus }) {
|
|
190
257
|
const [path, setPath] = useState('');
|
|
191
258
|
const [data, setData] = useState(null);
|
|
192
259
|
const [err, setErr] = useState('');
|
|
260
|
+
const panelRef = useRef(null);
|
|
193
261
|
|
|
194
262
|
const load = useCallback(async (p) => {
|
|
195
263
|
if (!p) { setData(null); setErr(''); return; }
|
|
@@ -199,10 +267,28 @@ function AccRulesPanel({ projects }) {
|
|
|
199
267
|
} catch (e) { setData(null); setErr(apiErr(e)); }
|
|
200
268
|
}, []);
|
|
201
269
|
|
|
270
|
+
// Deliberately keyed on `focus` alone: `projects` is rebuilt by the hub's
|
|
271
|
+
// own poll, and re-running this on every poll would re-scroll the page
|
|
272
|
+
// under the user every few seconds.
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (!focus || !focus.path) return;
|
|
275
|
+
// Prefer the hub's own spelling of the project so the <select> shows it;
|
|
276
|
+
// the store's spelling still loads when the project is not registered.
|
|
277
|
+
const known = (projects || []).find(p => accSamePath(p.path, focus.path));
|
|
278
|
+
const target = known ? known.path : focus.path;
|
|
279
|
+
setPath(target);
|
|
280
|
+
load(target);
|
|
281
|
+
const el = panelRef.current;
|
|
282
|
+
if (el && typeof el.scrollIntoView === 'function') {
|
|
283
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
284
|
+
}
|
|
285
|
+
}, [focus]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
286
|
+
|
|
287
|
+
const hot = focus && focus.rule ? String(focus.rule) : '';
|
|
202
288
|
const scopes = (data && data.rules) || {};
|
|
203
289
|
const layerRows = data && data.policy ? Object.entries(data.policy.layers || {}) : [];
|
|
204
290
|
return (
|
|
205
|
-
<div style={{
|
|
291
|
+
<div ref={panelRef} style={{
|
|
206
292
|
background: T.surface, border: `1px solid ${T.border}`, borderRadius: 10,
|
|
207
293
|
padding: '12px 16px',
|
|
208
294
|
}}>
|
|
@@ -249,14 +335,21 @@ function AccRulesPanel({ projects }) {
|
|
|
249
335
|
<div style={{ fontSize: 11.5, color: T.textDim }}>no rules</div>
|
|
250
336
|
) : (
|
|
251
337
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
252
|
-
{rows.map(([kind, glob], i) =>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
338
|
+
{rows.map(([kind, glob], i) => {
|
|
339
|
+
const lit = hot && glob === hot;
|
|
340
|
+
return (
|
|
341
|
+
<span key={i} className="mono" style={{
|
|
342
|
+
fontSize: 11, padding: '2px 8px', borderRadius: 4,
|
|
343
|
+
color: ACC_KIND_COLOR(kind),
|
|
344
|
+
background: `${ACC_KIND_COLOR(kind)}18`,
|
|
345
|
+
border: `1px solid ${ACC_KIND_COLOR(kind)}44`,
|
|
346
|
+
outline: lit ? `2px solid ${T.warn}` : 'none',
|
|
347
|
+
outlineOffset: lit ? 2 : 0,
|
|
348
|
+
}} title={lit ? `${kind} — this rule is costing you` : kind}>
|
|
349
|
+
{kind}: {glob}
|
|
350
|
+
</span>
|
|
351
|
+
);
|
|
352
|
+
})}
|
|
260
353
|
</div>
|
|
261
354
|
)}
|
|
262
355
|
{scope === 'builtin' && (sec.disabled || []).length > 0 && (
|
|
@@ -395,6 +488,11 @@ function HubAccess({ projects }) {
|
|
|
395
488
|
const [chip, setChip] = useState('pending');
|
|
396
489
|
const [busyId, setBusyId] = useState('');
|
|
397
490
|
const [confirmSpec, setConfirmSpec] = useState(null);
|
|
491
|
+
// `seq` makes a second click on the same rule re-fire the panel's effect.
|
|
492
|
+
const [ruleFocus, setRuleFocus] = useState(null);
|
|
493
|
+
const openRule = (r) => setRuleFocus({
|
|
494
|
+
path: r.project_path, rule: r.rule, seq: Date.now(),
|
|
495
|
+
});
|
|
398
496
|
|
|
399
497
|
const load = useCallback(async () => {
|
|
400
498
|
try {
|
|
@@ -459,6 +557,8 @@ function HubAccess({ projects }) {
|
|
|
459
557
|
))}
|
|
460
558
|
</div>
|
|
461
559
|
|
|
560
|
+
<AccCostStrip onOpenRule={openRule} />
|
|
561
|
+
|
|
462
562
|
{!loaded ? (
|
|
463
563
|
<div style={{ fontSize: 12, color: T.textDim }}>Loading…</div>
|
|
464
564
|
) : rows.length === 0 ? (
|
|
@@ -479,7 +579,7 @@ function HubAccess({ projects }) {
|
|
|
479
579
|
)}
|
|
480
580
|
|
|
481
581
|
<AccGrantsPanel projects={projects} />
|
|
482
|
-
<AccRulesPanel projects={projects} />
|
|
582
|
+
<AccRulesPanel projects={projects} focus={ruleFocus} />
|
|
483
583
|
{confirmSpec && (
|
|
484
584
|
<CredConfirm spec={confirmSpec} onClose={() => setConfirmSpec(null)} />
|
|
485
585
|
)}
|
{code_context_control-2.126.1 → code_context_control-2.127.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.127.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
|
|
@@ -260,6 +260,7 @@ services/ollama_client.py
|
|
|
260
260
|
services/ollama_credentials.py
|
|
261
261
|
services/oracle_service.py
|
|
262
262
|
services/output_filter.py
|
|
263
|
+
services/override_costs.py
|
|
263
264
|
services/override_grants.py
|
|
264
265
|
services/override_policy.py
|
|
265
266
|
services/override_requests.py
|
|
@@ -419,6 +420,7 @@ tests/test_mobile_api.py
|
|
|
419
420
|
tests/test_mobile_clients.py
|
|
420
421
|
tests/test_mobile_extras.py
|
|
421
422
|
tests/test_mobile_jobs_ci.py
|
|
423
|
+
tests/test_mobile_override_costs.py
|
|
422
424
|
tests/test_mobile_override_routes.py
|
|
423
425
|
tests/test_mobile_security_api.py
|
|
424
426
|
tests/test_no_stale_model_pins.py
|
|
@@ -443,6 +445,7 @@ tests/test_oracle_session_bootstrap.py
|
|
|
443
445
|
tests/test_oracle_subproject_awareness.py
|
|
444
446
|
tests/test_oracle_ui_bundle.py
|
|
445
447
|
tests/test_output_filter.py
|
|
448
|
+
tests/test_override_costs.py
|
|
446
449
|
tests/test_override_grants.py
|
|
447
450
|
tests/test_override_grants_mcp.py
|
|
448
451
|
tests/test_override_requests.py
|
|
@@ -71,6 +71,11 @@ from services.atomic_json import write_json_atomic
|
|
|
71
71
|
# reports the caller as `client: {kind, client_id}`, and override
|
|
72
72
|
# decisions are attributed to that kind ("desk" for the tray client,
|
|
73
73
|
# "mobile" for a phone or the legacy Discovery token).
|
|
74
|
+
# 5 (unchanged, additive, v2.127.0): `override_costs` capability and
|
|
75
|
+
# GET /overrides/costs — per-(project, rule) request counts over a
|
|
76
|
+
# trailing window, the "this rule is costing you" nudge (D3a). The
|
|
77
|
+
# /overrides/<id> detail also carries `allow_session_grants`,
|
|
78
|
+
# `max_ttl_s` and `session_confirm` at the top level.
|
|
74
79
|
API_VERSION = 5
|
|
75
80
|
|
|
76
81
|
CAPABILITIES = [
|
|
@@ -79,7 +84,7 @@ CAPABILITIES = [
|
|
|
79
84
|
"credentials", "credentials_write",
|
|
80
85
|
"access", "access_write",
|
|
81
86
|
"enforcement", "enforcement_write",
|
|
82
|
-
"override", "override_write",
|
|
87
|
+
"override", "override_write", "override_costs",
|
|
83
88
|
"edits", "locks", "status",
|
|
84
89
|
"insights", "insights_write",
|
|
85
90
|
"suggestions", "suggestions_write",
|
|
@@ -103,6 +108,9 @@ _CAPABILITY_SWITCHES = {
|
|
|
103
108
|
"enforcement_write": "mobile_access_write",
|
|
104
109
|
"override": "mobile_override_enabled",
|
|
105
110
|
"override_write": "mobile_override_write",
|
|
111
|
+
# The cost view is a read over the same store the inbox serves, so it
|
|
112
|
+
# goes dark with the inbox rather than carrying a switch of its own.
|
|
113
|
+
"override_costs": "mobile_override_enabled",
|
|
106
114
|
# The ops reads (edits, locks, status, insights, suggestions, review) are
|
|
107
115
|
# deliberately absent: they expose nothing the feed does not already, so
|
|
108
116
|
# gating them would only cost a client a capability probe. Only the two
|
|
@@ -2439,6 +2447,60 @@ def _write_override_section(project: Path, section: dict) -> dict:
|
|
|
2439
2447
|
return block
|
|
2440
2448
|
|
|
2441
2449
|
|
|
2450
|
+
def _days_arg(default: int, ceiling: int) -> int:
|
|
2451
|
+
"""``?days=`` clamped to ``[1, ceiling]``; garbage reads as the default.
|
|
2452
|
+
|
|
2453
|
+
A clamp rather than a 400 because the value only sizes a window — a
|
|
2454
|
+
client asking for a quarter gets the month it is allowed, and the answer
|
|
2455
|
+
echoes the window actually used so it can say so.
|
|
2456
|
+
"""
|
|
2457
|
+
try:
|
|
2458
|
+
want = int(request.args.get("days", default))
|
|
2459
|
+
except (TypeError, ValueError):
|
|
2460
|
+
want = default
|
|
2461
|
+
return max(1, min(ceiling, want))
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
@bp.route("/overrides/costs", methods=["GET"])
|
|
2465
|
+
def mobile_overrides_costs():
|
|
2466
|
+
"""Per-(project, rule) request counts over a trailing window (§11 T5).
|
|
2467
|
+
|
|
2468
|
+
The "this rule is costing you" number. Query: project (optional — omit
|
|
2469
|
+
for every project this gateway serves), days (1..30, default 7).
|
|
2470
|
+
Response: {days, rules: [{project_path, rule, rule_class, layer, count,
|
|
2471
|
+
approved, denied, expired, pending, last_at, suggestion}], count,
|
|
2472
|
+
project}. Rows sort by count desc then last_at desc; the desktop shows
|
|
2473
|
+
`suggestion` verbatim and nudges at count >= 3.
|
|
2474
|
+
|
|
2475
|
+
Registered BEFORE `/overrides/<id>` for the same reason `policy` is: the
|
|
2476
|
+
ordering is explicit so `costs` can never become a request id.
|
|
2477
|
+
"""
|
|
2478
|
+
gate = _feature_or_404("override_costs")
|
|
2479
|
+
if gate:
|
|
2480
|
+
return gate
|
|
2481
|
+
from services import override_costs as ocost
|
|
2482
|
+
raw_project = (request.args.get("project") or "").strip()
|
|
2483
|
+
project_path = ""
|
|
2484
|
+
if raw_project:
|
|
2485
|
+
resolved, err = _project_or_404(raw_project)
|
|
2486
|
+
if err:
|
|
2487
|
+
return err
|
|
2488
|
+
project_path = str(resolved)
|
|
2489
|
+
days = _days_arg(ocost.DEFAULT_DAYS, ocost.MAX_DAYS)
|
|
2490
|
+
try:
|
|
2491
|
+
rules = ocost.rule_costs(project_path=project_path or None, days=days)
|
|
2492
|
+
except Exception as exc:
|
|
2493
|
+
return _svc_error(exc)
|
|
2494
|
+
if not project_path:
|
|
2495
|
+
# Same rule as /overrides and /jobs: the store is one file for the
|
|
2496
|
+
# whole machine, and a cross-project view must not name a project
|
|
2497
|
+
# this token cannot otherwise see.
|
|
2498
|
+
rules = [r for r in rules
|
|
2499
|
+
if _resolve_registered(str(r.get("project_path") or "")) is not None]
|
|
2500
|
+
return jsonify({"days": days, "rules": rules, "count": len(rules),
|
|
2501
|
+
"project": project_path})
|
|
2502
|
+
|
|
2503
|
+
|
|
2442
2504
|
@bp.route("/overrides/<request_id>", methods=["GET"])
|
|
2443
2505
|
def mobile_override_get(request_id):
|
|
2444
2506
|
"""One request, full context."""
|
|
@@ -2481,6 +2543,15 @@ def mobile_override_get(request_id):
|
|
|
2481
2543
|
"confirm_with": (
|
|
2482
2544
|
row.get("rule") if str(row.get("rule_class") or "")
|
|
2483
2545
|
in opol.TYPED_CONFIRM_LAYERS else ""),
|
|
2546
|
+
# Top-level as well as under `policy` (v2.127.0): what a session-grant
|
|
2547
|
+
# button needs, in one fetch — whether the switch is on, how long the
|
|
2548
|
+
# grant would last, and the literal the client must send as
|
|
2549
|
+
# `confirm` (`decide()` demands it on the layers that are otherwise
|
|
2550
|
+
# one-tap). Duplicated on purpose: a client should not need the
|
|
2551
|
+
# policy dict's shape to draw one button.
|
|
2552
|
+
"allow_session_grants": bool(policy.allow_session_grants),
|
|
2553
|
+
"max_ttl_s": int(policy.max_ttl_s),
|
|
2554
|
+
"session_confirm": orq.CONFIRM_SESSION,
|
|
2484
2555
|
})
|
|
2485
2556
|
|
|
2486
2557
|
|
|
@@ -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.127.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"
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Override costs — the "this rule is costing you" nudge (spec §11 Threat 5).
|
|
2
|
+
|
|
3
|
+
The request store answers "what is waiting?"; nothing answered "which rule
|
|
4
|
+
keeps asking?". On 2026-09-06 one session filed six holds on
|
|
5
|
+
``**/.claude/skills/**`` for ONE file in 65 minutes — four approved by hand,
|
|
6
|
+
two lost to the TTL — and every surface rendered each card as if it were the
|
|
7
|
+
first. Approval fatigue is the threat §11 calls "the real one", and its
|
|
8
|
+
mitigation is a number: how often a rule held, and how the human answered.
|
|
9
|
+
|
|
10
|
+
This module is that number. It is a READ-ONLY fold over
|
|
11
|
+
``~/.c3/oracle/override_requests.json`` grouped by ``(project_path, rule)``
|
|
12
|
+
over a trailing window — never a decision, never a mutation, not even the
|
|
13
|
+
store's own lazy expiry flip (a lapsed pending row is *counted* as expired
|
|
14
|
+
against the supplied clock; flipping it on disk stays with the readers that
|
|
15
|
+
already do so). A corrupt or missing store reads as no rows, which is the
|
|
16
|
+
honest answer for a nudge: silence, not a crash in a route or a tray.
|
|
17
|
+
|
|
18
|
+
Consumers: ``GET /api/mobile/overrides/costs`` (desktop tray nudge),
|
|
19
|
+
``GET /api/hub/overrides/costs`` (Hub "Costing you" strip),
|
|
20
|
+
``c3 override costs``. The desktop shows ``suggestion`` verbatim, so its
|
|
21
|
+
three values are part of the wire contract.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from datetime import datetime, timedelta
|
|
26
|
+
|
|
27
|
+
from services import override_grants as og
|
|
28
|
+
from services import override_requests as orq
|
|
29
|
+
|
|
30
|
+
#: Window defaults. ``MAX_DAYS`` is the remote ceiling (the gateway clamps
|
|
31
|
+
#: to it); the CLI may ask for more, because a person at the keyboard asking
|
|
32
|
+
#: for a quarter's history is not a phone hammering the store.
|
|
33
|
+
DEFAULT_DAYS = 7
|
|
34
|
+
MAX_DAYS = 30
|
|
35
|
+
|
|
36
|
+
#: How many holds per rule per window before a surface should say something.
|
|
37
|
+
#: Shared by the desktop client (hard-coded there to the same value) and the
|
|
38
|
+
#: Hub strip; a rule that held twice is a coincidence, three times is a cost.
|
|
39
|
+
NUDGE_THRESHOLD = 3
|
|
40
|
+
|
|
41
|
+
#: The one-line verdicts. The desktop renders them verbatim — change the
|
|
42
|
+
#: wording here and the tray changes with it, so treat them as contract.
|
|
43
|
+
SUGGEST_ALLOW = "convert to allow"
|
|
44
|
+
SUGGEST_TIGHTEN = "tighten or deny"
|
|
45
|
+
SUGGEST_REVIEW = "review"
|
|
46
|
+
|
|
47
|
+
#: Output row shape, in order. ``count`` is EVERY request in the window
|
|
48
|
+
#: (withdrawn included); the four buckets are the human-visible outcomes and
|
|
49
|
+
#: deliberately leave ``withdrawn`` out — the agent cancelling its own
|
|
50
|
+
#: question says nothing about whether the rule costs the user anything.
|
|
51
|
+
FIELDS = (
|
|
52
|
+
"project_path", "rule", "rule_class", "layer",
|
|
53
|
+
"count", "approved", "denied", "expired", "pending",
|
|
54
|
+
"last_at", "suggestion",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def suggestion(approved: int, denied: int) -> str:
|
|
59
|
+
"""The verdict for one rule's window.
|
|
60
|
+
|
|
61
|
+
``convert to allow`` — the user keeps saying yes and has never said no;
|
|
62
|
+
the rule is friction, not protection, for this project. ``tighten or
|
|
63
|
+
deny`` — the user has said no at least twice; the rule is doing its job
|
|
64
|
+
and the agent should stop asking, or the rule should be narrower so the
|
|
65
|
+
legitimate case stops tripping it. Anything else is ``review``.
|
|
66
|
+
"""
|
|
67
|
+
if denied >= 2:
|
|
68
|
+
return SUGGEST_TIGHTEN
|
|
69
|
+
if approved >= 3 and denied == 0:
|
|
70
|
+
return SUGGEST_ALLOW
|
|
71
|
+
return SUGGEST_REVIEW
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _effective_status(row: dict, at: datetime) -> str:
|
|
75
|
+
"""A pending row past its ``expires_at`` IS expired, whether or not a
|
|
76
|
+
writer has flipped it yet. Evaluated against the caller's clock so a
|
|
77
|
+
fixed ``now`` in a test sees the same answer the live store would."""
|
|
78
|
+
status = str(row.get("status") or "")
|
|
79
|
+
if status == orq.STATUS_PENDING:
|
|
80
|
+
exp = og.parse_ts(row.get("expires_at"))
|
|
81
|
+
if exp is None or at >= exp:
|
|
82
|
+
return orq.STATUS_EXPIRED
|
|
83
|
+
return status
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _project_key(raw: str, cache: dict) -> str:
|
|
87
|
+
"""Grouping identity for a project path — same canonicaliser
|
|
88
|
+
``list_requests`` filters with, so a row spelled ``Y:/Projects/X`` and a
|
|
89
|
+
filter spelled ``y:\\projects\\x`` land in one group. Empty stays empty:
|
|
90
|
+
``path_key('')`` would resolve to the CWD and silently adopt orphan rows."""
|
|
91
|
+
raw = str(raw or "")
|
|
92
|
+
if not raw:
|
|
93
|
+
return ""
|
|
94
|
+
if raw not in cache:
|
|
95
|
+
try:
|
|
96
|
+
cache[raw] = og.path_key(raw) or raw.replace("\\", "/").lower()
|
|
97
|
+
except Exception:
|
|
98
|
+
cache[raw] = raw.replace("\\", "/").lower()
|
|
99
|
+
return cache[raw]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _load_rows() -> list:
|
|
103
|
+
"""Every request row, or ``[]`` — a nudge must never raise."""
|
|
104
|
+
try:
|
|
105
|
+
rows = orq.load()
|
|
106
|
+
except Exception:
|
|
107
|
+
return []
|
|
108
|
+
return [r for r in rows if isinstance(r, dict)]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def rule_costs(project_path: str | None = None, days: int = DEFAULT_DAYS,
|
|
112
|
+
now: datetime | None = None) -> list:
|
|
113
|
+
"""Per-``(project_path, rule)`` request counts over the trailing window.
|
|
114
|
+
|
|
115
|
+
``project_path`` narrows to one project (any spelling); ``None`` / ``""``
|
|
116
|
+
means every project in the store — callers serving a token MUST filter
|
|
117
|
+
the result to what that token may see (the store is one file for the
|
|
118
|
+
whole machine). ``days`` is the window length in days, floor 1; the
|
|
119
|
+
window is ``[now - days, now]`` on ``created_at``. ``now`` defaults to
|
|
120
|
+
the wall clock.
|
|
121
|
+
|
|
122
|
+
Returns a list of dicts with exactly :data:`FIELDS`, sorted by ``count``
|
|
123
|
+
descending then ``last_at`` descending. Never raises on store trouble.
|
|
124
|
+
"""
|
|
125
|
+
at = now or og.now()
|
|
126
|
+
try:
|
|
127
|
+
span = max(1, int(days))
|
|
128
|
+
except (TypeError, ValueError):
|
|
129
|
+
span = DEFAULT_DAYS
|
|
130
|
+
since = at - timedelta(days=span)
|
|
131
|
+
|
|
132
|
+
cache: dict = {}
|
|
133
|
+
want = _project_key(project_path or "", cache) if project_path else ""
|
|
134
|
+
|
|
135
|
+
groups: dict = {}
|
|
136
|
+
for row in _load_rows():
|
|
137
|
+
created = og.parse_ts(row.get("created_at"))
|
|
138
|
+
if created is None or created < since or created > at:
|
|
139
|
+
continue
|
|
140
|
+
proj_key = _project_key(row.get("project_path", ""), cache)
|
|
141
|
+
if want and proj_key != want:
|
|
142
|
+
continue
|
|
143
|
+
rule = str(row.get("rule") or "")
|
|
144
|
+
key = (proj_key, rule)
|
|
145
|
+
grp = groups.get(key)
|
|
146
|
+
if grp is None:
|
|
147
|
+
grp = groups[key] = {
|
|
148
|
+
"project_path": str(row.get("project_path") or ""),
|
|
149
|
+
"rule": rule,
|
|
150
|
+
"rule_class": str(row.get("rule_class") or ""),
|
|
151
|
+
"layer": str(row.get("layer") or ""),
|
|
152
|
+
"count": 0, "approved": 0, "denied": 0,
|
|
153
|
+
"expired": 0, "pending": 0,
|
|
154
|
+
"last_at": "", "_last_dt": None,
|
|
155
|
+
}
|
|
156
|
+
grp["count"] += 1
|
|
157
|
+
status = _effective_status(row, at)
|
|
158
|
+
if status in ("approved", "denied", "expired", "pending"):
|
|
159
|
+
grp[status] += 1
|
|
160
|
+
if grp["_last_dt"] is None or created > grp["_last_dt"]:
|
|
161
|
+
grp["_last_dt"] = created
|
|
162
|
+
grp["last_at"] = str(row.get("created_at") or "")
|
|
163
|
+
# The newest row is the most honest source for the labels — a
|
|
164
|
+
# rule reclassified mid-window reports what it is NOW.
|
|
165
|
+
grp["rule_class"] = str(row.get("rule_class") or grp["rule_class"])
|
|
166
|
+
grp["layer"] = str(row.get("layer") or grp["layer"])
|
|
167
|
+
grp["project_path"] = str(row.get("project_path") or grp["project_path"])
|
|
168
|
+
|
|
169
|
+
out = []
|
|
170
|
+
for grp in groups.values():
|
|
171
|
+
grp["suggestion"] = suggestion(grp["approved"], grp["denied"])
|
|
172
|
+
out.append(grp)
|
|
173
|
+
# count desc, then most recent first. Every group has a `_last_dt` (the
|
|
174
|
+
# first row that opened it set one), so the fallback never fires; it is
|
|
175
|
+
# there so a sort key can never be None.
|
|
176
|
+
out.sort(key=lambda g: (-g["count"],
|
|
177
|
+
-(g["_last_dt"] or since).timestamp()))
|
|
178
|
+
return [{k: grp[k] for k in FIELDS} for grp in out]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def costing(rules: list, threshold: int = NUDGE_THRESHOLD) -> list:
|
|
182
|
+
"""The subset a surface should actually nudge about."""
|
|
183
|
+
return [r for r in rules if int(r.get("count") or 0) >= threshold]
|