code-context-control 2.154.0__tar.gz → 2.157.1__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.154.0 → code_context_control-2.157.1}/PKG-INFO +3 -2
- {code_context_control-2.154.0 → code_context_control-2.157.1}/README.md +1 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/c3.py +92 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/commands/parser.py +50 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/tools.html +45 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_session_open.py +101 -2
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_server.py +196 -226
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/app.js +3 -1
- code_context_control-2.157.1/cli/hub_ui/components/hub_schedules.js +364 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/topbar.js +1 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/mcp_server.py +174 -3
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/server.py +57 -35
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/_grants.py +18 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/edit.py +2 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/locks.py +1 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/project.py +18 -0
- code_context_control-2.157.1/cli/tools/schedule.py +226 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/PKG-INFO +3 -2
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/SOURCES.txt +27 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/requires.txt +3 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/host.py +13 -3
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/config.py +26 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/oracle_server.py +24 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/mobile_api.py +238 -4
- code_context_control-2.157.1/oracle/services/mobile_fleet.py +588 -0
- code_context_control-2.157.1/oracle/services/schedule_api.py +255 -0
- code_context_control-2.157.1/oracle/services/scheduler.py +320 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/pyproject.toml +2 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/agent_locks.py +8 -5
- code_context_control-2.157.1/services/cron_expr.py +292 -0
- code_context_control-2.157.1/services/fleet_overview.py +479 -0
- code_context_control-2.157.1/services/host_handoff.py +320 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/notifications.py +3 -1
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/project_manager.py +41 -41
- code_context_control-2.157.1/services/schedule_approvals.py +202 -0
- code_context_control-2.157.1/services/schedule_runner.py +264 -0
- code_context_control-2.157.1/services/schedule_store.py +601 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/session_catalog.py +29 -12
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/session_live.py +16 -4
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/shell_jobs.py +31 -7
- code_context_control-2.157.1/services/ui_registry.py +264 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_agent_locks.py +42 -0
- code_context_control-2.157.1/tests/test_cron_expr.py +145 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_session_end.py +74 -0
- code_context_control-2.157.1/tests/test_host_handoff.py +165 -0
- code_context_control-2.157.1/tests/test_host_rebind.py +126 -0
- code_context_control-2.157.1/tests/test_hub_schedules_routes.py +111 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_clients.py +1 -1
- code_context_control-2.157.1/tests/test_mobile_fleet.py +580 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_override_costs.py +1 -1
- code_context_control-2.157.1/tests/test_mobile_schedules.py +144 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_security_api.py +16 -4
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_notification_kinds.py +1 -1
- code_context_control-2.157.1/tests/test_project_lease_identity.py +48 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_project_manager.py +56 -0
- code_context_control-2.157.1/tests/test_schedule_api.py +163 -0
- code_context_control-2.157.1/tests/test_schedule_approvals.py +86 -0
- code_context_control-2.157.1/tests/test_schedule_mcp.py +106 -0
- code_context_control-2.157.1/tests/test_schedule_runner.py +209 -0
- code_context_control-2.157.1/tests/test_schedule_store.py +250 -0
- code_context_control-2.157.1/tests/test_scheduler_tick.py +257 -0
- code_context_control-2.157.1/tests/test_shell_jobs_argv.py +73 -0
- code_context_control-2.157.1/tests/test_ui_registry.py +179 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/LICENSE +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/_hook_utils.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/_shell_writes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/commands/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/commands/common.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/docs.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/edits.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/access.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/ci.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/credentials.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/delegate.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/index.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/jira.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/masking.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/oracle.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/sessions.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/shared.css +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/guide/workflow.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_agent_model.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_artifact.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_c3read.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_codex_lifecycle.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_filter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_read.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_session_end.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_access.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_cred_audit.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_sessions.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/hub_tokens.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/hub_ui.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/progress.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/_edit_report.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/agent.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/ci.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/compress.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/delegate.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/edits.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/federate.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/filter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/impact.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/jira.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/memory.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/override.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/read.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/search.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/session.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/shell.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/shell_job.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/shell_nudge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/shell_parsers.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/shell_render.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/status.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/tasks.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/tools/validate.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/api.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/app.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/access.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/icons.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/shared.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui/theme.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui_legacy.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/cli/ui_nano.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/codex_patch.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/config.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/grok_payload.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/hook_command.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/ide.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/mcp_toml.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/core/web_security.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/listeners.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/chat_poll.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/client_tokens.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/local_session.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/activity.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/agents.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/app.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/busy.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/core.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/header.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/insights.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/projects.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/settings.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/access_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/access_telemetry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/activity_log.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/agent_base.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/agent_downshift.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/agents.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/agents_workflow.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/artifact_defs.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/artifact_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/atomic_json.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/auto_memory.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/background_service.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/delegate_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/delegate_write_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/map_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/search_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bench/shell_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bitbucket_client.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/builtin_leases.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_act.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_cache.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_expr.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_failures.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_github.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_impact.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_intel.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_runner.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ci_workflow.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/circuit_breaker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/claude_md.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/codex_integration.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/codex_transcript.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/compressor.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/context_snapshot.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/conversation_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/cred_audit.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/cred_telemetry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/credential_backup.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/credential_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/delegate_agents.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/delegate_hints.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/delegate_write.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/dir_map.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/doc_index.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/e2e_tasks.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/edit_blobs.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/edit_ledger.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/embedding_index.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/enforcement_policy.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/error_reporting.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/file_map.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/file_memory.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/git_context.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/grok_integration.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/grok_transcript.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/host_sessions.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/hub_service.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/index_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/indexer.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/jira_client.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/jira_cloud.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/jira_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/jira_data_center.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/jira_links.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/lexical_index.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/mask_activation.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/mask_mirror.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/mask_presets.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_consolidator.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_distiller.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_graph.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_grounder.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_queue.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/memory_scorer.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/metrics.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ollama_bridge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ollama_client.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/ollama_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/oracle_service.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/output_filter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/override_costs.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/override_grants.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/override_policy.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/override_requests.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/override_wake.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/parser.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/project_runtime.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/protocol.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/proxy_state.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/read_stamps.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/repo_map.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/repo_shape.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/reranker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/retention.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/retrieval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/retrieval_broker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/router.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/runtime.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/scanner.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/session_benchmark.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/session_manager.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/session_preloader.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/shell_output.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/subprojects.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/task_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/telemetry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/terminal_launch.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/text_index.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/time_tracker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/tool_classifier.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/transcript_index.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/validation_cache.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/vector_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/watcher.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/services/win_subprocess.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/setup.cfg +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_activity_log_find_last.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_atomic_json.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_background_service.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_builtin_leases.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_builtin_modes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_act.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_cache.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_dag_ownership.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_expr.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_impact.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_intel.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_claude_md_updater.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_codex_mcp_transport.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_codex_native.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_confirm_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_confirm_guard_wiring.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_cooccurrence_bounds.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_cred_audit.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_cred_telemetry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credential_backup.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credential_login_kind.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credential_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credential_ui_parity.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_adoption_d4.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_auto.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_claude_d1.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_grok.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_host_d2.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_scout_d3.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_telemetry_d0.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_thinking_cap.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_tuning.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_write.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_delegate_write_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_dir_map_c3.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_eol_atomic.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_outcome.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_report.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_revert.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_stale.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_embedding_index_corrupt_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_embedding_store_safety.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_env_parser.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_failed_calls_do_not_unlock.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_file_map_c1.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_grok_native.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_codex_compat.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_grok_compat.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_state.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_access_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_builtin_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_hook_migration.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_ide_cmd.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_oracle_service_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_override_policy_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_override_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_rules_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_sessions_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_index_cap_not_hardcoded.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_init_clear_preserves_user_content.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_init_scan.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_client.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_links.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_large_files_c4.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_map_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_map_telemetry_c0.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_memory_system.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_extras.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_jobs_ci.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_mobile_override_routes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_health_probe.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_loopback.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_output_filter.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_override_costs.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_override_grants.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_override_requests.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_override_wake.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_permissions.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_pm_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_project_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_quarantine_sweep.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_read_outside_root.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_related_facts.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_repo_map.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_repo_shape.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_retention.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_review_digest.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p1.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p2.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p2b.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p3.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p4.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_search_p5.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_service_durability.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_session_budget.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_session_catalog.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_session_live.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_session_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_budget_s1.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_eval.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_jobs_s3.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_nudge_s4.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_output.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_parsers_s2.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_telemetry_s0.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_write_ledger.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_shell_writes.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subproject_clear_scope.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subproject_depth_consumers.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subproject_linkage_api.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_subprojects.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_task_store.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_task_tool.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_token_tracking.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_ui_jsx_syntax.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_validate.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_version_sync.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_watcher_pruning.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_web_security.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/backend.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/main.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/__init__.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/index_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/init_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/search_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/session_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/stats.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.154.0 → code_context_control-2.157.1}/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.157.1
|
|
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
|
|
@@ -45,6 +45,7 @@ Requires-Dist: click>=8.0.0
|
|
|
45
45
|
Requires-Dist: pyyaml>=6.0.0
|
|
46
46
|
Requires-Dist: keyring>=24.0
|
|
47
47
|
Requires-Dist: cryptography>=41.0
|
|
48
|
+
Requires-Dist: tzdata>=2024.1; sys_platform == "win32"
|
|
48
49
|
Provides-Extra: vector
|
|
49
50
|
Requires-Dist: scikit-learn>=1.3.0; extra == "vector"
|
|
50
51
|
Requires-Dist: numpy>=1.24.0; extra == "vector"
|
|
@@ -391,7 +392,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
391
392
|
|
|
392
393
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
393
394
|
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
394
|
-
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing,
|
|
395
|
+
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, any way to loosen or disable a built-in guard (v2.157.0 adds read, tighten and end-a-lease-early only), lock force-release, and the raw-content access preview. Since v2.157.0 (API version 7) the gateway also serves the cross-project views C3 Desk reads from the Hub (locks, Access Guard rules, tool discipline, override policy, built-in guards, the session catalog without transcript text, and a Now snapshot), so the companion app can show what Desk shows.
|
|
395
396
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
396
397
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
397
398
|
- **API keys** for third-party providers are read from the environment and never persisted by C3.
|
|
@@ -324,7 +324,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
324
324
|
|
|
325
325
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
326
326
|
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
327
|
-
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing,
|
|
327
|
+
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, any way to loosen or disable a built-in guard (v2.157.0 adds read, tighten and end-a-lease-early only), lock force-release, and the raw-content access preview. Since v2.157.0 (API version 7) the gateway also serves the cross-project views C3 Desk reads from the Hub (locks, Access Guard rules, tool discipline, override policy, built-in guards, the session catalog without transcript text, and a Now snapshot), so the companion app can show what Desk shows.
|
|
328
328
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
329
329
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
330
330
|
- **API keys** for third-party providers are read from the environment and never persisted by C3.
|
|
@@ -91,7 +91,7 @@ console = Console() if HAS_RICH else None
|
|
|
91
91
|
# Config
|
|
92
92
|
CONFIG_DIR = ".c3"
|
|
93
93
|
CONFIG_FILE = ".c3/config.json"
|
|
94
|
-
__version__ = "2.
|
|
94
|
+
__version__ = "2.157.1"
|
|
95
95
|
|
|
96
96
|
# The PreToolUse matcher for native subagent calls (installer and hub migration).
|
|
97
97
|
AGENT_MATCHER = "Agent|Task"
|
|
@@ -264,6 +264,7 @@ _C3_MCP_ALLOW = [
|
|
|
264
264
|
"mcp__c3__c3_jira", "mcp__c3__c3_credentials",
|
|
265
265
|
"mcp__c3__c3_project", "mcp__c3__c3_task", "mcp__c3__c3_artifacts",
|
|
266
266
|
"mcp__c3__c3_override", "mcp__c3__c3_ci", "mcp__c3__c3_shell_job",
|
|
267
|
+
"mcp__c3__c3_schedule",
|
|
267
268
|
]
|
|
268
269
|
|
|
269
270
|
# Obsolete MCP tool names from earlier C3 versions. `c3 permissions clean`
|
|
@@ -7235,6 +7236,95 @@ def _enforce_audit(result: dict, project_path: str) -> None:
|
|
|
7235
7236
|
_OVERRIDE_DEFAULT_TOOL = {"read": "Read", "write": "Edit"}
|
|
7236
7237
|
|
|
7237
7238
|
|
|
7239
|
+
def cmd_schedule(args):
|
|
7240
|
+
"""`c3 schedule` — scheduled tasks: list, add, run, approve or deny (docs/scheduled-tasks.md)."""
|
|
7241
|
+
import json as _json
|
|
7242
|
+
|
|
7243
|
+
from cli.tools.schedule import build_target, build_when
|
|
7244
|
+
from services.schedule_approvals import ApprovalError, ApprovalStore
|
|
7245
|
+
from services.schedule_store import ScheduleError, ScheduleStore
|
|
7246
|
+
|
|
7247
|
+
sub = getattr(args, "schedule_cmd", None)
|
|
7248
|
+
if not sub:
|
|
7249
|
+
print("Usage: c3 schedule <list|add|show|rm|enable|disable|run|runs|approvals|approve|deny> ...")
|
|
7250
|
+
return
|
|
7251
|
+
store = ScheduleStore()
|
|
7252
|
+
approvals = ApprovalStore()
|
|
7253
|
+
try:
|
|
7254
|
+
if sub == "list":
|
|
7255
|
+
rows = store.list()
|
|
7256
|
+
if not rows:
|
|
7257
|
+
print("No schedules. Add one with `c3 schedule add`.")
|
|
7258
|
+
for row in rows:
|
|
7259
|
+
when = row.get("when") or {}
|
|
7260
|
+
cadence = when.get("expr") or (f"every {when.get('seconds')}s" if when.get("type") == "every"
|
|
7261
|
+
else when.get("when", ""))
|
|
7262
|
+
flag = "on " if row.get("enabled") else "off"
|
|
7263
|
+
print(f" {row['id']} [{flag}] {row['name']!r} {row['kind']} @ {cadence} {row['autonomy']} "
|
|
7264
|
+
f"next={row.get('next_run_at') or '-'} last={row.get('last_status') or '-'} "
|
|
7265
|
+
f"{row['project_path']}")
|
|
7266
|
+
elif sub == "show":
|
|
7267
|
+
row = store.get(args.schedule_id)
|
|
7268
|
+
if row is None:
|
|
7269
|
+
raise ScheduleError(f"no schedule {args.schedule_id}")
|
|
7270
|
+
print(_json.dumps(row, indent=2, ensure_ascii=False))
|
|
7271
|
+
elif sub == "add":
|
|
7272
|
+
from oracle.config import load_config
|
|
7273
|
+
from oracle.services.scheduler import defaults_from_config
|
|
7274
|
+
when = build_when(cron=args.cron, every=args.every, at=args.at, preset=args.preset, tz=args.tz,
|
|
7275
|
+
preset_at=args.preset_at, days=args.days)
|
|
7276
|
+
if when is None:
|
|
7277
|
+
raise ScheduleError("give one of --cron, --every, --at or --preset")
|
|
7278
|
+
fields = {
|
|
7279
|
+
"name": args.name, "kind": args.kind, "target": build_target(args.kind, args.target),
|
|
7280
|
+
"provider": args.provider, "model": args.model, "effort": args.effort,
|
|
7281
|
+
"autonomy": args.autonomy, "project_path": str(Path(args.project_path).resolve()),
|
|
7282
|
+
"when": when, "overlap": args.overlap, "check_command": args.check_command,
|
|
7283
|
+
}
|
|
7284
|
+
if args.timeout_s:
|
|
7285
|
+
fields["timeout_s"] = args.timeout_s
|
|
7286
|
+
if args.max_budget_usd:
|
|
7287
|
+
fields["max_budget_usd"] = args.max_budget_usd
|
|
7288
|
+
if args.permission_mode:
|
|
7289
|
+
fields["permission_mode"] = args.permission_mode
|
|
7290
|
+
row = store.create(fields, defaults=defaults_from_config(load_config()), created_by="cli")
|
|
7291
|
+
print(f"Created {row['id']} {row['name']!r}; next run {row['next_run_at']}")
|
|
7292
|
+
elif sub in ("enable", "disable"):
|
|
7293
|
+
row = store.set_enabled(args.schedule_id, sub == "enable", by="cli")
|
|
7294
|
+
print(f"{row['id']} {sub}d; next run {row.get('next_run_at') or '-'}")
|
|
7295
|
+
elif sub == "rm":
|
|
7296
|
+
if not store.delete(args.schedule_id, by="cli"):
|
|
7297
|
+
raise ScheduleError(f"no schedule {args.schedule_id}")
|
|
7298
|
+
print(f"Deleted {args.schedule_id}")
|
|
7299
|
+
elif sub == "run":
|
|
7300
|
+
if store.get(args.schedule_id) is None:
|
|
7301
|
+
raise ScheduleError(f"no schedule {args.schedule_id}")
|
|
7302
|
+
run = store.request_run(args.schedule_id, trigger="manual", by="cli", approved=args.bypass_approval)
|
|
7303
|
+
print(f"Queued run {run['run_id']} ({run['status']}); the Oracle dispatches it on its next tick")
|
|
7304
|
+
elif sub == "runs":
|
|
7305
|
+
rows = store.runs(schedule_id=args.schedule_id or None, status=args.status or None, limit=args.limit)
|
|
7306
|
+
if not rows:
|
|
7307
|
+
print("No runs.")
|
|
7308
|
+
for run in rows:
|
|
7309
|
+
cost = f" ${run['cost_usd']:.3f}" if run.get("cost_usd") is not None else ""
|
|
7310
|
+
err = f" — {str(run['error'])[:80]}" if run.get("error") else ""
|
|
7311
|
+
print(f" {run['run_id']} {run['status']:<17} {run['trigger']:<8} {run['schedule_name']!r} "
|
|
7312
|
+
f"{(run.get('claimed_at') or '')[:19]}{cost}{err}")
|
|
7313
|
+
elif sub == "approvals":
|
|
7314
|
+
rows = approvals.list(status=args.status or "pending")
|
|
7315
|
+
if not rows:
|
|
7316
|
+
print(f"No {args.status or 'pending'} approvals.")
|
|
7317
|
+
for row in rows:
|
|
7318
|
+
print(f" {row['id']} {row['status']:<8} {row['schedule_name']!r} {row['summary']} "
|
|
7319
|
+
f"expires={row['expires_at']}")
|
|
7320
|
+
elif sub in ("approve", "deny"):
|
|
7321
|
+
row = approvals.decide(args.approval_id, sub, decided_by="cli", note=args.note or "")
|
|
7322
|
+
print(f"{row['id']} {row['status']}; the Oracle acts on it on its next tick")
|
|
7323
|
+
except (ScheduleError, ApprovalError) as exc:
|
|
7324
|
+
print(f"Error: {exc}")
|
|
7325
|
+
sys.exit(1)
|
|
7326
|
+
|
|
7327
|
+
|
|
7238
7328
|
def cmd_override(args):
|
|
7239
7329
|
"""`c3 override` — Override Requests: allow one blocked call, once.
|
|
7240
7330
|
|
|
@@ -9663,6 +9753,7 @@ def main():
|
|
|
9663
9753
|
"access": cmd_access,
|
|
9664
9754
|
"enforce": cmd_enforce,
|
|
9665
9755
|
"override": cmd_override,
|
|
9756
|
+
"schedule": cmd_schedule,
|
|
9666
9757
|
"locks": cmd_locks,
|
|
9667
9758
|
"prune-quarantine": cmd_prune_quarantine,
|
|
9668
9759
|
"ci": cmd_ci,
|
|
@@ -840,6 +840,56 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
840
840
|
ov_costs.add_argument("--path", dest="project_path", default=".",
|
|
841
841
|
help="Project directory (default: current)")
|
|
842
842
|
|
|
843
|
+
# ── Scheduled tasks (docs/scheduled-tasks.md) ─────────────────────────
|
|
844
|
+
# The human CLI over the same stores the Oracle fires from. approve/deny
|
|
845
|
+
# live here and on the Hub/Desk/phone, never on the c3_schedule agent tool.
|
|
846
|
+
p_schedule = subparsers.add_parser(
|
|
847
|
+
"schedule", help="Scheduled tasks — skills, prompts or commands the Oracle fires on a cadence")
|
|
848
|
+
sched_subs = p_schedule.add_subparsers(dest="schedule_cmd")
|
|
849
|
+
sched_subs.add_parser("list", help="Every schedule on this machine")
|
|
850
|
+
sc_show = sched_subs.add_parser("show", help="One schedule as JSON")
|
|
851
|
+
sc_show.add_argument("schedule_id")
|
|
852
|
+
sc_add = sched_subs.add_parser("add", help="Create a schedule")
|
|
853
|
+
sc_add.add_argument("name")
|
|
854
|
+
sc_add.add_argument("target", help="'/skill args', a prompt, or a command (see --kind)")
|
|
855
|
+
sc_add.add_argument("--kind", choices=["skill", "prompt", "command"], default="skill")
|
|
856
|
+
sc_add.add_argument("--cron", default="", help="5-field cron, e.g. '0 9 * * 1-5'")
|
|
857
|
+
sc_add.add_argument("--every", type=int, default=0, help="Seconds between runs")
|
|
858
|
+
sc_add.add_argument("--at", default="", help="One-shot ISO datetime")
|
|
859
|
+
sc_add.add_argument("--preset", choices=["hourly", "daily", "weekdays", "weekly", "monthly"], default="")
|
|
860
|
+
sc_add.add_argument("--preset-at", dest="preset_at", default="09:00", help="HH:MM for --preset")
|
|
861
|
+
sc_add.add_argument("--days", default="", help="Weekly preset days, e.g. mon,fri")
|
|
862
|
+
sc_add.add_argument("--tz", default="local", help="IANA zone or 'local'")
|
|
863
|
+
sc_add.add_argument("--provider", choices=["claude", "codex", "gemini", "grok"], default="claude")
|
|
864
|
+
sc_add.add_argument("--model", default="")
|
|
865
|
+
sc_add.add_argument("--effort", choices=["", "low", "medium", "high", "xhigh", "max"], default="")
|
|
866
|
+
sc_add.add_argument("--autonomy", choices=["autonomous", "approval"], default="approval")
|
|
867
|
+
sc_add.add_argument("--permission-mode", dest="permission_mode", default="")
|
|
868
|
+
sc_add.add_argument("--timeout", dest="timeout_s", type=int, default=0)
|
|
869
|
+
sc_add.add_argument("--budget", dest="max_budget_usd", type=float, default=0.0, help="--max-budget-usd cap")
|
|
870
|
+
sc_add.add_argument("--overlap", choices=["skip", "allow"], default="skip")
|
|
871
|
+
sc_add.add_argument("--check", dest="check_command", default="",
|
|
872
|
+
help="Command whose exit 1 arms the run; exit 0 skips it")
|
|
873
|
+
sc_add.add_argument("--path", dest="project_path", default=".", help="Project directory (default: current)")
|
|
874
|
+
for verb, help_text in (("enable", "Resume a schedule"), ("disable", "Pause a schedule"),
|
|
875
|
+
("rm", "Delete a schedule")):
|
|
876
|
+
sp = sched_subs.add_parser(verb, help=help_text)
|
|
877
|
+
sp.add_argument("schedule_id")
|
|
878
|
+
sc_run = sched_subs.add_parser("run", help="Queue a run now")
|
|
879
|
+
sc_run.add_argument("schedule_id")
|
|
880
|
+
sc_run.add_argument("--bypass-approval", dest="bypass_approval", action="store_true",
|
|
881
|
+
help="Skip the approval gate for this one run")
|
|
882
|
+
sc_runs = sched_subs.add_parser("runs", help="Recent runs")
|
|
883
|
+
sc_runs.add_argument("--schedule", dest="schedule_id", default="")
|
|
884
|
+
sc_runs.add_argument("--status", default="")
|
|
885
|
+
sc_runs.add_argument("--limit", type=int, default=30)
|
|
886
|
+
sc_appr = sched_subs.add_parser("approvals", help="Pending (or --status) approvals")
|
|
887
|
+
sc_appr.add_argument("--status", default="")
|
|
888
|
+
for verb in ("approve", "deny"):
|
|
889
|
+
sp = sched_subs.add_parser(verb, help=f"{verb.capitalize()} one pending scheduled run")
|
|
890
|
+
sp.add_argument("approval_id")
|
|
891
|
+
sp.add_argument("--note", default="")
|
|
892
|
+
|
|
843
893
|
# ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
|
|
844
894
|
# LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
|
|
845
895
|
# `c3 access` (path policy — a security boundary) and from
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
<a href="#c3_impact" class="sidebar-link"><span class="icon">💥</span> c3_impact</a>
|
|
108
108
|
<a href="#c3_shell" class="sidebar-link"><span class="icon">⚡</span> c3_shell</a>
|
|
109
109
|
<a href="#c3_shell_job" class="sidebar-link"><span class="icon">🕐</span> c3_shell_job</a>
|
|
110
|
+
<a href="#c3_schedule" class="sidebar-link"><span class="icon">📅</span> c3_schedule</a>
|
|
110
111
|
<a href="#c3_ci" class="sidebar-link"><span class="icon">🧪</span> c3_ci</a>
|
|
111
112
|
</div>
|
|
112
113
|
<div class="sidebar-section">
|
|
@@ -166,6 +167,7 @@
|
|
|
166
167
|
<a href="#c3_impact" class="toc-item">c3_impact <span class="cat">Analysis</span></a>
|
|
167
168
|
<a href="#c3_shell" class="toc-item">c3_shell <span class="cat">Execute</span></a>
|
|
168
169
|
<a href="#c3_shell_job" class="toc-item">c3_shell_job <span class="cat">Execute</span></a>
|
|
170
|
+
<a href="#c3_schedule" class="toc-item">c3_schedule <span class="cat">Execute</span></a>
|
|
169
171
|
<a href="#c3_ci" class="toc-item">c3_ci <span class="cat">Execute</span></a>
|
|
170
172
|
<a href="#c3_edit" class="toc-item">c3_edit <span class="cat">Write</span></a>
|
|
171
173
|
<a href="#c3_validate" class="toc-item">c3_validate <span class="cat">Write</span></a>
|
|
@@ -1602,6 +1604,49 @@ c3_task(action=<span class="str">'note_add'</span>, note=<span class="str">'Kanb
|
|
|
1602
1604
|
</div>
|
|
1603
1605
|
</div>
|
|
1604
1606
|
|
|
1607
|
+
<!-- c3_schedule -->
|
|
1608
|
+
<div class="tool-card" id="c3_schedule">
|
|
1609
|
+
<div class="tool-card-header">
|
|
1610
|
+
<span class="tool-name">c3_schedule</span>
|
|
1611
|
+
<div class="tag-row">
|
|
1612
|
+
<span class="badge badge-purple">execute</span>
|
|
1613
|
+
<span class="badge">v2.157.1</span>
|
|
1614
|
+
</div>
|
|
1615
|
+
<span class="tool-tagline">Scheduled tasks — a skill, prompt or command the Oracle fires on a cadence, autonomously or after a human approves each run</span>
|
|
1616
|
+
</div>
|
|
1617
|
+
<div class="tool-card-body">
|
|
1618
|
+
<p class="tool-desc">A schedule names what to run (<code>kind='skill'</code> with <code>target='/daily_report weekly'</code>, a free <code>prompt</code>, or a plain <code>command</code> with no LLM), where (a registered project), how (<code>provider</code> claude / codex / gemini / grok, <code>model</code>, <code>effort</code>, <code>permission_mode</code>, <code>max_budget_usd</code>) and when (<code>cron</code>, <code>preset</code>, <code>every</code> seconds, or a one-shot <code>at</code>). The Oracle's scheduler claims each due slot once, runs it as a detached shell job in the project, records exit, duration, cost and tokens, and writes a <code>kind=schedule</code> notification the Hub, Desk and phone show. <code>autonomy='approval'</code> (the default from an agent) creates a pending approval instead of firing; nobody approving before the TTL means the slot is logged <code>expired</code> and skipped. Deciding is human-only — the Hub Schedules tab, the Desk, the phone or <code>c3 schedule approve</code>. See <code>docs/scheduled-tasks.md</code>.</p>
|
|
1619
|
+
|
|
1620
|
+
<h4>Actions</h4>
|
|
1621
|
+
<table class="params-table">
|
|
1622
|
+
<thead><tr><th>Action</th><th>Group</th><th>Description</th></tr></thead>
|
|
1623
|
+
<tbody>
|
|
1624
|
+
<tr><td class="param-name">create</td><td>Write</td><td class="param-desc"><code>name</code>, <code>kind</code>, <code>target</code>, one cadence (<code>cron</code> + <code>tz</code> | <code>preset</code> + <code>preset_at</code> [+ <code>days</code>] | <code>every</code> | <code>at</code>), then <code>provider</code>, <code>model</code>, <code>effort</code>, <code>autonomy</code>, <code>project</code>, <code>cwd</code>, <code>timeout</code>, <code>max_budget_usd</code>, <code>permission_mode</code>, <code>allowed_tools</code>, <code>check_command</code> (exit 1 arms the run, exit 0 skips it for free), <code>overlap</code>, <code>misfire_grace_s</code></td></tr>
|
|
1625
|
+
<tr><td class="param-name param-list">update, enable, disable, delete</td><td>Write</td><td class="param-desc"><code>schedule_id</code> + changed fields; a disabled schedule keeps its history</td></tr>
|
|
1626
|
+
<tr><td class="param-name">run_now</td><td>Write</td><td class="param-desc">Queue one run outside the cadence; approval-mode schedules still ask</td></tr>
|
|
1627
|
+
<tr><td class="param-name param-list">list, get, runs, approvals, status</td><td>Read</td><td class="param-desc">Schedules, one row, recent runs ([<code>schedule_id</code>] [<code>status</code>] [<code>limit</code>]), pending approvals, the loop's state</td></tr>
|
|
1628
|
+
<tr><td class="param-name">preview</td><td>Read</td><td class="param-desc">A cadence → its next fire times, before you commit to it</td></tr>
|
|
1629
|
+
</tbody>
|
|
1630
|
+
</table>
|
|
1631
|
+
|
|
1632
|
+
<h4>Examples</h4>
|
|
1633
|
+
<pre><code><span class="com"># A weekday report, sonnet at medium effort, asking first</span>
|
|
1634
|
+
c3_schedule(action=<span class="str">'create'</span>, name=<span class="str">'daily report'</span>, kind=<span class="str">'skill'</span>,
|
|
1635
|
+
target=<span class="str">'/daily_report'</span>, preset=<span class="str">'weekdays'</span>, preset_at=<span class="str">'09:00'</span>,
|
|
1636
|
+
model=<span class="str">'sonnet'</span>, effort=<span class="str">'medium'</span>, max_budget_usd=<span class="num">1.0</span>)
|
|
1637
|
+
|
|
1638
|
+
<span class="com"># A free command every hour, no LLM, fires on its own</span>
|
|
1639
|
+
c3_schedule(action=<span class="str">'create'</span>, name=<span class="str">'fetch'</span>, kind=<span class="str">'command'</span>,
|
|
1640
|
+
target=<span class="str">'git fetch --all --prune'</span>, every=<span class="num">3600</span>, autonomy=<span class="str">'autonomous'</span>)
|
|
1641
|
+
|
|
1642
|
+
<span class="com"># See what a cron would do, then what ran</span>
|
|
1643
|
+
c3_schedule(action=<span class="str">'preview'</span>, cron=<span class="str">'0 18 * * 1-5'</span>, tz=<span class="str">'Europe/Berlin'</span>)
|
|
1644
|
+
c3_schedule(action=<span class="str">'runs'</span>, limit=<span class="num">10</span>)</code></pre>
|
|
1645
|
+
|
|
1646
|
+
<p class="tool-desc"><strong>Surfaces:</strong> the Hub's Schedules tab (create, edit, pause, run now, approve or deny), <code>/api/mobile/schedules</code> for the Desk and the phone, and <code>c3 schedule</code> on the command line.</p>
|
|
1647
|
+
</div>
|
|
1648
|
+
</div>
|
|
1649
|
+
|
|
1605
1650
|
<!-- c3_artifacts -->
|
|
1606
1651
|
<div class="tool-card" id="c3_artifacts">
|
|
1607
1652
|
<div class="tool-card-header">
|
|
@@ -30,6 +30,12 @@ the hub, and it skips a project that already has a live UI port, so a
|
|
|
30
30
|
``C3_NO_UI_AUTOSTART=1`` or
|
|
31
31
|
``.c3/config.json → {"ui": {"autostart_on_session": false}}``.
|
|
32
32
|
|
|
33
|
+
A ``clear`` (or an in-session ``resume``) keeps the MCP server it already
|
|
34
|
+
has, under a host session id that just ended. This hook finds that server
|
|
35
|
+
by the host pid both of them share and hands it the new id
|
|
36
|
+
(``services.host_handoff``, v2.155.0); the server then writes the
|
|
37
|
+
``C3 connected <new id>`` row the desktop sentinel is waiting for.
|
|
38
|
+
|
|
33
39
|
``source: "compact"`` writes NOTHING: a compaction is the same session and
|
|
34
40
|
the same MCP process, and a "Session started" toast on every compaction
|
|
35
41
|
would teach the user to ignore the real ones.
|
|
@@ -48,6 +54,8 @@ from __future__ import annotations
|
|
|
48
54
|
import json
|
|
49
55
|
import os
|
|
50
56
|
import sys
|
|
57
|
+
import time
|
|
58
|
+
import uuid
|
|
51
59
|
from pathlib import Path
|
|
52
60
|
|
|
53
61
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
@@ -56,6 +64,11 @@ from cli._hook_utils import detect_host, find_project, log_hook_error # noqa: E
|
|
|
56
64
|
from cli.hook_session_end import short_id # noqa: E402
|
|
57
65
|
|
|
58
66
|
_SILENT_SOURCES = {"compact"}
|
|
67
|
+
# How long one session's UI launch holds the project against the next. A
|
|
68
|
+
# server lands in the registry only after it binds its port, a few seconds
|
|
69
|
+
# after Popen; this covers that gap with room to spare. After it, the live
|
|
70
|
+
# registry entry (``_ui_running``) is the guard again.
|
|
71
|
+
_LAUNCH_GRACE_S = 90.0
|
|
59
72
|
|
|
60
73
|
|
|
61
74
|
def _ui_autostart_enabled(project: Path) -> bool:
|
|
@@ -96,7 +109,10 @@ def _ui_running(pm, project: Path) -> bool:
|
|
|
96
109
|
"""True when a live UI server is already registered for this project.
|
|
97
110
|
|
|
98
111
|
This is what keeps `resume` / `clear` — both real SessionStart events —
|
|
99
|
-
from stacking a second server on every reconnect.
|
|
112
|
+
from stacking a second server on every reconnect. A registered server
|
|
113
|
+
whose port is quiet but whose pid is running is still "running": it is
|
|
114
|
+
starting up, or busy, and a second one would only be the leak this
|
|
115
|
+
check exists to prevent (2.155.0).
|
|
100
116
|
"""
|
|
101
117
|
target = os.path.normcase(str(project))
|
|
102
118
|
for entry in pm._read_registry():
|
|
@@ -107,8 +123,72 @@ def _ui_running(pm, project: Path) -> bool:
|
|
|
107
123
|
same = os.path.normcase(str(Path(raw).resolve())) == target
|
|
108
124
|
except Exception:
|
|
109
125
|
same = os.path.normcase(raw) == target
|
|
110
|
-
if same
|
|
126
|
+
if not same or not entry.get("port"):
|
|
127
|
+
continue
|
|
128
|
+
if pm._port_alive(entry["port"]):
|
|
129
|
+
return True
|
|
130
|
+
if entry.get("pid") and pm._pid_alive(entry.get("pid")):
|
|
131
|
+
return True
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _launch_lock(project: Path) -> Path:
|
|
136
|
+
return project / ".c3" / "ui_launch.lock"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _claim_launch(project: Path) -> bool:
|
|
140
|
+
"""Atomically claim the right to launch this project's UI server.
|
|
141
|
+
|
|
142
|
+
``_ui_running`` only sees a server once it has registered its port, a
|
|
143
|
+
few seconds after launch. Sessions that open inside that window (an
|
|
144
|
+
agent spawning several at once, or a resume racing a startup) each saw
|
|
145
|
+
no live UI and each launched one, which is how a project ended up with
|
|
146
|
+
two servers. The first claimant creates ``.c3/ui_launch.lock`` with
|
|
147
|
+
O_EXCL; anyone else inside ``_LAUNCH_GRACE_S`` stands down.
|
|
148
|
+
|
|
149
|
+
A lock older than the grace window is stale (the launch died, or the
|
|
150
|
+
server since exited). It is taken over by renaming it to a unique name,
|
|
151
|
+
which only one contender can do, then the create is retried. If the
|
|
152
|
+
lock cannot be handled at all the answer is True, which is the
|
|
153
|
+
behaviour from before this lock existed: a missing UI is worse than a
|
|
154
|
+
rare duplicate.
|
|
155
|
+
"""
|
|
156
|
+
lock = _launch_lock(project)
|
|
157
|
+
try:
|
|
158
|
+
lock.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
except Exception:
|
|
160
|
+
return True
|
|
161
|
+
for _ in range(3):
|
|
162
|
+
try:
|
|
163
|
+
fd = os.open(str(lock), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
|
|
164
|
+
except FileExistsError:
|
|
165
|
+
try:
|
|
166
|
+
age = time.time() - lock.stat().st_mtime
|
|
167
|
+
except FileNotFoundError:
|
|
168
|
+
continue # released between our create and stat; retry
|
|
169
|
+
except Exception:
|
|
170
|
+
return True
|
|
171
|
+
if age < _LAUNCH_GRACE_S:
|
|
172
|
+
return False
|
|
173
|
+
stale = lock.with_name(f"ui_launch.stale-{uuid.uuid4().hex}")
|
|
174
|
+
try:
|
|
175
|
+
os.rename(str(lock), str(stale))
|
|
176
|
+
except FileNotFoundError:
|
|
177
|
+
continue # another contender took it over first
|
|
178
|
+
except Exception:
|
|
179
|
+
return False
|
|
180
|
+
try:
|
|
181
|
+
stale.unlink()
|
|
182
|
+
except Exception:
|
|
183
|
+
pass
|
|
184
|
+
continue
|
|
185
|
+
except Exception:
|
|
111
186
|
return True
|
|
187
|
+
try:
|
|
188
|
+
os.write(fd, f"{os.getpid()} {time.time():.0f}\n".encode("ascii"))
|
|
189
|
+
finally:
|
|
190
|
+
os.close(fd)
|
|
191
|
+
return True
|
|
112
192
|
return False
|
|
113
193
|
|
|
114
194
|
|
|
@@ -136,6 +216,8 @@ def ensure_ui(project: Path, host_session_id: str = "") -> str:
|
|
|
136
216
|
return "unregistered"
|
|
137
217
|
if _ui_running(pm, project):
|
|
138
218
|
return "already-running"
|
|
219
|
+
if not _claim_launch(project):
|
|
220
|
+
return "launch-pending"
|
|
139
221
|
# launch_session copies os.environ into the detached child, which is
|
|
140
222
|
# where cli/server._register_session reads the ownership from.
|
|
141
223
|
os.environ["C3_UI_OWNER"] = "session"
|
|
@@ -150,6 +232,22 @@ def ensure_ui(project: Path, host_session_id: str = "") -> str:
|
|
|
150
232
|
return "error"
|
|
151
233
|
|
|
152
234
|
|
|
235
|
+
def hand_off_runtime(project: Path, host: str, host_sid: str, start_source: str) -> list:
|
|
236
|
+
"""Point the MCP server this host already runs at its new session id.
|
|
237
|
+
|
|
238
|
+
Returns the C3 session ids handed off (usually one, often none: a
|
|
239
|
+
``startup`` has no server yet). Never raises.
|
|
240
|
+
"""
|
|
241
|
+
if not host_sid:
|
|
242
|
+
return []
|
|
243
|
+
try:
|
|
244
|
+
from services.host_handoff import hand_off
|
|
245
|
+
return hand_off(project, host, host_sid, start_source)
|
|
246
|
+
except Exception as exc:
|
|
247
|
+
log_hook_error("hook_session_open", exc)
|
|
248
|
+
return []
|
|
249
|
+
|
|
250
|
+
|
|
153
251
|
def run(payload: dict, project_path: Path | None = None):
|
|
154
252
|
"""Core logic — importable by the dispatcher and tests. Returns None."""
|
|
155
253
|
start_source = str(payload.get("source") or "").strip().lower()
|
|
@@ -178,6 +276,7 @@ def run(payload: dict, project_path: Path | None = None):
|
|
|
178
276
|
kind="session", ref_id=host_sid)
|
|
179
277
|
except Exception:
|
|
180
278
|
pass
|
|
279
|
+
hand_off_runtime(project, host, host_sid, start_source)
|
|
181
280
|
ensure_ui(project, host_sid)
|
|
182
281
|
return None
|
|
183
282
|
|