novacode-cli 1.2.0__py3-none-any.whl
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.
- novacode_cli/__init__.py +29 -0
- novacode_cli/__main__.py +6 -0
- novacode_cli/_lazy_heavy.py +99 -0
- novacode_cli/_version.py +3 -0
- novacode_cli/agent_stream.py +178 -0
- novacode_cli/agents/agent_file.py +250 -0
- novacode_cli/agents/agent_mailbox.py +120 -0
- novacode_cli/agents/approval_decisions.py +146 -0
- novacode_cli/agents/arg_repair.py +196 -0
- novacode_cli/agents/async_agents/__init__.py +6 -0
- novacode_cli/agents/async_agents/_config.py +213 -0
- novacode_cli/agents/async_agents/_mcp_tools.py +192 -0
- novacode_cli/agents/async_agents/_specialist.py +84 -0
- novacode_cli/agents/async_agents/_test_commands.py +89 -0
- novacode_cli/agents/async_agents/_tools.py +432 -0
- novacode_cli/agents/async_agents/general_purpose.py +42 -0
- novacode_cli/agents/async_agents/langgraph.json +9 -0
- novacode_cli/agents/async_agents/research.py +22 -0
- novacode_cli/agents/async_context.py +303 -0
- novacode_cli/agents/async_on_demand.py +52 -0
- novacode_cli/agents/async_task_errors.py +75 -0
- novacode_cli/agents/async_workspace.py +27 -0
- novacode_cli/agents/attribution.py +165 -0
- novacode_cli/agents/browser_mode.py +119 -0
- novacode_cli/agents/btw_agent.py +71 -0
- novacode_cli/agents/caveman.py +352 -0
- novacode_cli/agents/caveman_support.py +370 -0
- novacode_cli/agents/code_mode.py +774 -0
- novacode_cli/agents/code_runtime.py +160 -0
- novacode_cli/agents/context_compaction.py +1339 -0
- novacode_cli/agents/core_agent.py +2710 -0
- novacode_cli/agents/default_subagents/__init__.py +24 -0
- novacode_cli/agents/default_subagents/async_subagents.py +296 -0
- novacode_cli/agents/default_subagents/builtin/browser-automation-agent.md +110 -0
- novacode_cli/agents/default_subagents/builtin/bug-fix-agent.md +22 -0
- novacode_cli/agents/default_subagents/builtin/bug-investigation-agent.md +42 -0
- novacode_cli/agents/default_subagents/builtin/code-explorer.md +37 -0
- novacode_cli/agents/default_subagents/builtin/code-review-agent.md +33 -0
- novacode_cli/agents/default_subagents/builtin/dependency-audit-agent.md +34 -0
- novacode_cli/agents/default_subagents/builtin/documentation-update-agent.md +46 -0
- novacode_cli/agents/default_subagents/builtin/performance-audit-agent.md +41 -0
- novacode_cli/agents/default_subagents/builtin/plan-scout-agent.md +64 -0
- novacode_cli/agents/default_subagents/builtin/refactoring-agent.md +35 -0
- novacode_cli/agents/default_subagents/builtin/refactoring-specialist-agent.md +175 -0
- novacode_cli/agents/default_subagents/builtin/security-audit-agent.md +96 -0
- novacode_cli/agents/default_subagents/builtin/test-generation-agent.md +33 -0
- novacode_cli/agents/default_subagents/builtin/test-runner-agent.md +55 -0
- novacode_cli/agents/default_subagents/prompt.py +88 -0
- novacode_cli/agents/default_subagents/subagents.py +138 -0
- novacode_cli/agents/dream_agent.py +93 -0
- novacode_cli/agents/edit_diagnostics.py +362 -0
- novacode_cli/agents/edit_fallback.py +62 -0
- novacode_cli/agents/handoff.py +166 -0
- novacode_cli/agents/invocation.py +152 -0
- novacode_cli/agents/model_budget.py +42 -0
- novacode_cli/agents/model_retry.py +113 -0
- novacode_cli/agents/model_router.py +420 -0
- novacode_cli/agents/openai_decisions.py +186 -0
- novacode_cli/agents/plan_agent/__init__.py +12 -0
- novacode_cli/agents/plan_agent/plan_agent.py +357 -0
- novacode_cli/agents/plan_agent/plan_mode_middleware.py +64 -0
- novacode_cli/agents/plan_execution.py +237 -0
- novacode_cli/agents/ponytail.py +66 -0
- novacode_cli/agents/prime.py +274 -0
- novacode_cli/agents/provider_caching.py +99 -0
- novacode_cli/agents/server_launcher.py +904 -0
- novacode_cli/agents/strict_tools.py +384 -0
- novacode_cli/agents/task_discipline.py +294 -0
- novacode_cli/agents/tool_call_repair.py +290 -0
- novacode_cli/agents/tool_offload.py +291 -0
- novacode_cli/agents/tool_search.py +806 -0
- novacode_cli/agents/tool_search_variants.py +106 -0
- novacode_cli/agents/tool_verdicts.py +1194 -0
- novacode_cli/agents/user_async_agents.py +312 -0
- novacode_cli/artifacts/__init__.py +10 -0
- novacode_cli/artifacts/registry.py +278 -0
- novacode_cli/artifacts/server.py +426 -0
- novacode_cli/assets/Nova.png +0 -0
- novacode_cli/assets/nova-avatar.png +0 -0
- novacode_cli/audio/__init__.py +301 -0
- novacode_cli/audio/capture.py +137 -0
- novacode_cli/audio/downloads.py +273 -0
- novacode_cli/audio/pipeline.py +391 -0
- novacode_cli/audio/playback.py +87 -0
- novacode_cli/audio/providers.py +208 -0
- novacode_cli/audio/speakable.py +78 -0
- novacode_cli/audio/stt.py +132 -0
- novacode_cli/audio/stt_deepgram.py +69 -0
- novacode_cli/audio/stt_elevenlabs.py +89 -0
- novacode_cli/audio/stt_parakeet.py +94 -0
- novacode_cli/audio/stt_whistle.py +61 -0
- novacode_cli/audio/summarize.py +92 -0
- novacode_cli/audio/tts.py +85 -0
- novacode_cli/audio/tts_kokoro.py +73 -0
- novacode_cli/audio/tts_orpheus.py +180 -0
- novacode_cli/audio/tts_pocket.py +131 -0
- novacode_cli/audio/tts_sano.py +69 -0
- novacode_cli/audio/vad.py +145 -0
- novacode_cli/backends/__init__.py +17 -0
- novacode_cli/backends/filesystem.py +894 -0
- novacode_cli/backends/native_search.py +92 -0
- novacode_cli/bootstrap/__init__.py +9 -0
- novacode_cli/bootstrap/env_snapshot.py +240 -0
- novacode_cli/bootstrap/steering.py +464 -0
- novacode_cli/bootstrap/vision_router.py +604 -0
- novacode_cli/brand.py +362 -0
- novacode_cli/cli_args.py +718 -0
- novacode_cli/commands/__init__.py +237 -0
- novacode_cli/commands/agents_commands.py +231 -0
- novacode_cli/commands/auth_handler.py +40 -0
- novacode_cli/commands/browser_use_handler.py +75 -0
- novacode_cli/commands/cache_handler.py +32 -0
- novacode_cli/commands/chat_handler.py +1357 -0
- novacode_cli/commands/ci_fix.py +171 -0
- novacode_cli/commands/claude_plan.py +331 -0
- novacode_cli/commands/codex_plan.py +195 -0
- novacode_cli/commands/commands.py +1113 -0
- novacode_cli/commands/create_handler.py +124 -0
- novacode_cli/commands/create_server.py +1887 -0
- novacode_cli/commands/cron_handler.py +119 -0
- novacode_cli/commands/dream_handler.py +203 -0
- novacode_cli/commands/effort_handler.py +65 -0
- novacode_cli/commands/evolution_handler.py +214 -0
- novacode_cli/commands/file_commands.py +381 -0
- novacode_cli/commands/handoff_handler.py +68 -0
- novacode_cli/commands/hooks_handler.py +269 -0
- novacode_cli/commands/init_handler.py +146 -0
- novacode_cli/commands/learning_handler.py +160 -0
- novacode_cli/commands/lite_mode_handler.py +76 -0
- novacode_cli/commands/log_commands.py +363 -0
- novacode_cli/commands/mcp_handler.py +29 -0
- novacode_cli/commands/model_handler.py +29 -0
- novacode_cli/commands/notifications_handler.py +158 -0
- novacode_cli/commands/onboard_handler.py +121 -0
- novacode_cli/commands/plan_handler.py +440 -0
- novacode_cli/commands/planner_usage.py +250 -0
- novacode_cli/commands/plugin_install_handler.py +38 -0
- novacode_cli/commands/plugins_handler.py +35 -0
- novacode_cli/commands/prime_handler.py +455 -0
- novacode_cli/commands/prompt_handler.py +149 -0
- novacode_cli/commands/provider_usage.py +160 -0
- novacode_cli/commands/ralph_events.py +106 -0
- novacode_cli/commands/ralph_handler.py +1293 -0
- novacode_cli/commands/refine_handler.py +213 -0
- novacode_cli/commands/research_handler.py +202 -0
- novacode_cli/commands/rootapp_remote.py +83 -0
- novacode_cli/commands/router_handler.py +36 -0
- novacode_cli/commands/server_commands.py +249 -0
- novacode_cli/commands/session_commands.py +203 -0
- novacode_cli/commands/share_handler.py +450 -0
- novacode_cli/commands/side_commands.py +366 -0
- novacode_cli/commands/skill_invoke.py +420 -0
- novacode_cli/commands/skills_commands.py +48 -0
- novacode_cli/commands/trace_handler.py +322 -0
- novacode_cli/commands/trello_handler.py +226 -0
- novacode_cli/commands/trello_remote.py +177 -0
- novacode_cli/commands/trello_server.py +1214 -0
- novacode_cli/commands/vision_handler.py +127 -0
- novacode_cli/commands/voice_handler.py +447 -0
- novacode_cli/commands/webhook_handler.py +134 -0
- novacode_cli/commands/wiki_commands.py +227 -0
- novacode_cli/compaction.py +753 -0
- novacode_cli/computation_cache.py +159 -0
- novacode_cli/computer/__init__.py +1 -0
- novacode_cli/computer/persistent.py +234 -0
- novacode_cli/computer/preview.py +253 -0
- novacode_cli/computer/runtime.py +474 -0
- novacode_cli/computer/windows_console.py +152 -0
- novacode_cli/computer/windows_qemu.py +211 -0
- novacode_cli/config/browser_mode.py +159 -0
- novacode_cli/config/catalog_models.py +95 -0
- novacode_cli/config/config.py +1388 -0
- novacode_cli/config/credentials.py +540 -0
- novacode_cli/config/google_oauth_auth.py +281 -0
- novacode_cli/config/model_capabilities.py +343 -0
- novacode_cli/config/model_catalog.py +275 -0
- novacode_cli/config/model_create.py +1325 -0
- novacode_cli/config/model_manager.py +695 -0
- novacode_cli/config/nova_config.py +1283 -0
- novacode_cli/config/openai_chatgpt_auth.py +384 -0
- novacode_cli/config/opencode_gateway.py +25 -0
- novacode_cli/config/plan_mode.py +188 -0
- novacode_cli/config/provider_auth.py +438 -0
- novacode_cli/config/provider_catalog.json +5127 -0
- novacode_cli/config/provider_registry.py +38 -0
- novacode_cli/config/provider_transports.py +258 -0
- novacode_cli/config/role_models.py +175 -0
- novacode_cli/context/__init__.py +50 -0
- novacode_cli/context/_analysis.py +799 -0
- novacode_cli/context/_budget.py +116 -0
- novacode_cli/context/_conversation.py +73 -0
- novacode_cli/context/_dynamic.py +264 -0
- novacode_cli/context/_model_config.py +380 -0
- novacode_cli/context/_models_dev.py +99 -0
- novacode_cli/context/archives.py +248 -0
- novacode_cli/context/estimates.py +65 -0
- novacode_cli/context/history.py +17 -0
- novacode_cli/context/knowledge_cache.py +292 -0
- novacode_cli/context/manager.py +101 -0
- novacode_cli/context/observations.py +65 -0
- novacode_cli/context/pressure.py +168 -0
- novacode_cli/context/provider_accounting.py +109 -0
- novacode_cli/context/snapshot.py +493 -0
- novacode_cli/context/strategies.py +279 -0
- novacode_cli/context/task_progress.py +122 -0
- novacode_cli/core/__init__.py +0 -0
- novacode_cli/core/agent_loop.py +1821 -0
- novacode_cli/core/autonomous_loop.py +277 -0
- novacode_cli/core/code_approvals.py +57 -0
- novacode_cli/core/input_preparation.py +276 -0
- novacode_cli/core/reasoning.py +67 -0
- novacode_cli/core/streaming.py +326 -0
- novacode_cli/core/subagent_tasks.py +696 -0
- novacode_cli/core/subagent_tracking.py +235 -0
- novacode_cli/core/tool_results.py +116 -0
- novacode_cli/core/verification_loop.py +272 -0
- novacode_cli/council.py +595 -0
- novacode_cli/council_consensus.py +115 -0
- novacode_cli/council_decision.py +157 -0
- novacode_cli/council_planner.py +1421 -0
- novacode_cli/council_settings.py +208 -0
- novacode_cli/council_web_settings.py +41 -0
- novacode_cli/council_workspace.py +56 -0
- novacode_cli/cowork/__init__.py +10 -0
- novacode_cli/cowork/broker_middleware.py +162 -0
- novacode_cli/cowork/launcher.py +102 -0
- novacode_cli/cowork/policy.py +284 -0
- novacode_cli/cowork/ui.py +826 -0
- novacode_cli/daemons/__init__.py +1 -0
- novacode_cli/daemons/registry.py +342 -0
- novacode_cli/desktop_notifications.py +302 -0
- novacode_cli/doctor.py +251 -0
- novacode_cli/embeddings.py +109 -0
- novacode_cli/entrypoint.py +56 -0
- novacode_cli/errors/__init__.py +19 -0
- novacode_cli/errors/handlers.py +648 -0
- novacode_cli/errors/provider_errors.py +249 -0
- novacode_cli/errors/taxonomy.py +80 -0
- novacode_cli/events.py +179 -0
- novacode_cli/file_ops.py +714 -0
- novacode_cli/git_safety.py +281 -0
- novacode_cli/handoff.py +383 -0
- novacode_cli/headless/__init__.py +18 -0
- novacode_cli/headless/input.py +58 -0
- novacode_cli/headless/output.py +224 -0
- novacode_cli/headless/pipe.py +532 -0
- novacode_cli/headless/runner.py +461 -0
- novacode_cli/hermes/__init__.py +39 -0
- novacode_cli/hermes/activation.py +297 -0
- novacode_cli/hermes/config.py +129 -0
- novacode_cli/hermes/curator.py +199 -0
- novacode_cli/hermes/evolution.py +374 -0
- novacode_cli/hermes/ledger.py +364 -0
- novacode_cli/hermes/memory_tiers.py +969 -0
- novacode_cli/hermes/middleware.py +678 -0
- novacode_cli/hermes/overview.py +174 -0
- novacode_cli/hermes/prompt_evolution.py +503 -0
- novacode_cli/hermes/refine_loop.py +643 -0
- novacode_cli/hermes/refinement_log.py +190 -0
- novacode_cli/hermes/review.py +674 -0
- novacode_cli/hermes/skill_debate.py +135 -0
- novacode_cli/hermes/skill_discovery.py +450 -0
- novacode_cli/hermes/skill_manager.py +335 -0
- novacode_cli/hermes/skill_proposals.py +43 -0
- novacode_cli/hermes/test_gaming.py +119 -0
- novacode_cli/hermes/tracker.py +435 -0
- novacode_cli/hermes/tuner.py +206 -0
- novacode_cli/hermes/verification_memory.py +134 -0
- novacode_cli/hermes/verifier.py +277 -0
- novacode_cli/hitl/__init__.py +22 -0
- novacode_cli/hitl/interrupts.py +493 -0
- novacode_cli/hooks.py +359 -0
- novacode_cli/image_utils.py +517 -0
- novacode_cli/init/__init__.py +7 -0
- novacode_cli/init/events.py +43 -0
- novacode_cli/input_utils.py +526 -0
- novacode_cli/install_commands.py +87 -0
- novacode_cli/integrations/__init__.py +82 -0
- novacode_cli/integrations/daytona.py +122 -0
- novacode_cli/integrations/docker.py +309 -0
- novacode_cli/integrations/host_path.py +111 -0
- novacode_cli/integrations/langsmith.py +151 -0
- novacode_cli/integrations/modal.py +134 -0
- novacode_cli/integrations/runloop.py +130 -0
- novacode_cli/integrations/sandbox_factory.py +1388 -0
- novacode_cli/integrations/sandbox_registry.py +312 -0
- novacode_cli/integrations/workdir_backend.py +589 -0
- novacode_cli/main.py +2078 -0
- novacode_cli/mcp/__init__.py +73 -0
- novacode_cli/mcp/auth.py +189 -0
- novacode_cli/mcp/client.py +571 -0
- novacode_cli/mcp/code_mode.py +5 -0
- novacode_cli/mcp/code_runtime.py +5 -0
- novacode_cli/mcp/commands.py +902 -0
- novacode_cli/mcp/config.py +406 -0
- novacode_cli/mcp/generic.py +128 -0
- novacode_cli/mcp/middleware.py +993 -0
- novacode_cli/mcp/presets.py +367 -0
- novacode_cli/memory/__init__.py +1 -0
- novacode_cli/memory/agent_memory.py +1312 -0
- novacode_cli/memory/builtin_lessons.py +60 -0
- novacode_cli/memory/dream_gates.py +192 -0
- novacode_cli/memory/limits.py +59 -0
- novacode_cli/memory/parsed_cache.py +106 -0
- novacode_cli/memory/recall_stats.py +97 -0
- novacode_cli/memory/semantic.py +116 -0
- novacode_cli/memory/store.py +335 -0
- novacode_cli/memory/wiki.py +274 -0
- novacode_cli/migrate.py +185 -0
- novacode_cli/onboarding.py +649 -0
- novacode_cli/onboarding_check.py +119 -0
- novacode_cli/path_approval.py +297 -0
- novacode_cli/plan_archive.py +148 -0
- novacode_cli/plugins/__init__.py +121 -0
- novacode_cli/plugins/claude_plugins.py +538 -0
- novacode_cli/plugins/cli.py +168 -0
- novacode_cli/plugins/components.py +413 -0
- novacode_cli/plugins/hooks.py +356 -0
- novacode_cli/plugins/loader.py +317 -0
- novacode_cli/plugins/marketplaces.py +199 -0
- novacode_cli/plugins/storage.py +70 -0
- novacode_cli/prime.py +759 -0
- novacode_cli/prime_factory/__init__.py +15 -0
- novacode_cli/prime_factory/container.py +198 -0
- novacode_cli/prime_factory/controller.py +565 -0
- novacode_cli/prime_factory/knowledge.py +261 -0
- novacode_cli/prime_factory/publication.py +218 -0
- novacode_cli/prime_factory/recovery.py +50 -0
- novacode_cli/prime_factory/research.py +370 -0
- novacode_cli/prime_factory/runner.py +608 -0
- novacode_cli/prime_factory/runtime.py +261 -0
- novacode_cli/prime_factory/state.py +201 -0
- novacode_cli/prime_factory/store.py +128 -0
- novacode_cli/prime_factory/worker.py +129 -0
- novacode_cli/prime_factory/workflow.py +899 -0
- novacode_cli/prime_factory/workspace.py +317 -0
- novacode_cli/prime_memory.py +290 -0
- novacode_cli/process_manager.py +585 -0
- novacode_cli/project_onboard.py +537 -0
- novacode_cli/prompts/System_Prompt_Nova.jinja +23 -0
- novacode_cli/prompts/__init__.py +168 -0
- novacode_cli/prompts/agent_definitions.jinja +11 -0
- novacode_cli/prompts/agent_generation.jinja +125 -0
- novacode_cli/prompts/async_agents/general_purpose.jinja +22 -0
- novacode_cli/prompts/claude_plan_mode.jinja +12 -0
- novacode_cli/prompts/coding_workflow.jinja +18 -0
- novacode_cli/prompts/computer_use.jinja +17 -0
- novacode_cli/prompts/core_agent_system.jinja +81 -0
- novacode_cli/prompts/delegation.jinja +15 -0
- novacode_cli/prompts/desktop_power.jinja +4 -0
- novacode_cli/prompts/file_paths.jinja +15 -0
- novacode_cli/prompts/file_tracker.jinja +23 -0
- novacode_cli/prompts/init_agents_md.jinja +56 -0
- novacode_cli/prompts/init_exploration.jinja +174 -0
- novacode_cli/prompts/init_semantic_orchestrate.jinja +37 -0
- novacode_cli/prompts/instruction_scope.jinja +5 -0
- novacode_cli/prompts/learnings_extraction.jinja +45 -0
- novacode_cli/prompts/longterm_memory.jinja +54 -0
- novacode_cli/prompts/mcp.jinja +84 -0
- novacode_cli/prompts/memory_consolidation.jinja +40 -0
- novacode_cli/prompts/nova_evolution.jinja +73 -0
- novacode_cli/prompts/nova_review.jinja +158 -0
- novacode_cli/prompts/nova_skill_debate.jinja +36 -0
- novacode_cli/prompts/nova_standing_instructions.jinja +26 -0
- novacode_cli/prompts/nova_verify.jinja +100 -0
- novacode_cli/prompts/onboard_dashboard.jinja +378 -0
- novacode_cli/prompts/plan_agent.jinja +87 -0
- novacode_cli/prompts/plan_scouts.jinja +5 -0
- novacode_cli/prompts/plan_storage.jinja +8 -0
- novacode_cli/prompts/planning.jinja +82 -0
- novacode_cli/prompts/ponytail/LICENSE +21 -0
- novacode_cli/prompts/ponytail/UPSTREAM.json +15 -0
- novacode_cli/prompts/ponytail/hooks/ponytail-config.js +175 -0
- novacode_cli/prompts/ponytail/hooks/ponytail-instructions.js +98 -0
- novacode_cli/prompts/ponytail/skills/ponytail/SKILL.md +52 -0
- novacode_cli/prompts/ponytail/skills/ponytail-audit/SKILL.md +103 -0
- novacode_cli/prompts/ponytail/skills/ponytail-debt/SKILL.md +44 -0
- novacode_cli/prompts/ponytail/skills/ponytail-gain/SKILL.md +52 -0
- novacode_cli/prompts/ponytail/skills/ponytail-help/SKILL.md +76 -0
- novacode_cli/prompts/ponytail/skills/ponytail-review/SKILL.md +101 -0
- novacode_cli/prompts/prompt_evolution.jinja +27 -0
- novacode_cli/prompts/ralph_iteration.jinja +75 -0
- novacode_cli/prompts/refine_gate.jinja +31 -0
- novacode_cli/prompts/refine_plan.jinja +64 -0
- novacode_cli/prompts/refine_review.jinja +34 -0
- novacode_cli/prompts/research_delivery.jinja +9 -0
- novacode_cli/prompts/research_swarm.jinja +130 -0
- novacode_cli/prompts/response_style.jinja +5 -0
- novacode_cli/prompts/session_summarization.jinja +86 -0
- novacode_cli/prompts/skill_creation.jinja +85 -0
- novacode_cli/prompts/subagents/fact_checker.jinja +48 -0
- novacode_cli/prompts/subagents/financial_analyst.jinja +50 -0
- novacode_cli/prompts/subagents/literature_reviewer.jinja +52 -0
- novacode_cli/prompts/subagents/market_analyst.jinja +48 -0
- novacode_cli/prompts/subagents/research_synthesizer.jinja +57 -0
- novacode_cli/prompts/subagents/technical_researcher.jinja +56 -0
- novacode_cli/prompts/subagents/web_researcher.jinja +46 -0
- novacode_cli/prompts/summarization.jinja +107 -0
- novacode_cli/prompts/summarization_answers.jinja +23 -0
- novacode_cli/prompts/summarization_questions.jinja +32 -0
- novacode_cli/prompts/vision_caption.jinja +21 -0
- novacode_cli/prompts/voice_summary.jinja +65 -0
- novacode_cli/prompts/wiki_file.jinja +57 -0
- novacode_cli/prompts/wiki_ingest.jinja +87 -0
- novacode_cli/recovery.py +383 -0
- novacode_cli/remote/__init__.py +45 -0
- novacode_cli/remote/approvals.py +282 -0
- novacode_cli/remote/async_task_clients.py +97 -0
- novacode_cli/remote/async_task_watcher.py +240 -0
- novacode_cli/remote/bridge.py +905 -0
- novacode_cli/remote/command_menu.py +26 -0
- novacode_cli/remote/command_ui.py +243 -0
- novacode_cli/remote/config.py +199 -0
- novacode_cli/remote/discord_bridge.py +530 -0
- novacode_cli/remote/discord_format.py +31 -0
- novacode_cli/remote/discord_threads.py +225 -0
- novacode_cli/remote/images.py +97 -0
- novacode_cli/remote/processor.py +601 -0
- novacode_cli/remote/rootapp_bot/bot.cjs +16 -0
- novacode_cli/remote/rootapp_bot/package-lock.json +2683 -0
- novacode_cli/remote/rootapp_bot/package.json +18 -0
- novacode_cli/remote/rootapp_bot/transport.cjs +80 -0
- novacode_cli/remote/rootapp_bridge.py +370 -0
- novacode_cli/remote/rootapp_setup.py +89 -0
- novacode_cli/remote/routing.py +103 -0
- novacode_cli/remote/scheduler.py +302 -0
- novacode_cli/remote/session.py +164 -0
- novacode_cli/remote/status.py +411 -0
- novacode_cli/remote/streaming.py +188 -0
- novacode_cli/remote/telegram_bridge.py +1118 -0
- novacode_cli/remote/telegram_format.py +215 -0
- novacode_cli/remote/telegram_hub.py +256 -0
- novacode_cli/remote/voice_notes.py +120 -0
- novacode_cli/remote/webhook_adapters.py +167 -0
- novacode_cli/remote/webhook_server.py +154 -0
- novacode_cli/rewind.py +150 -0
- novacode_cli/rooms/__init__.py +1 -0
- novacode_cli/rooms/bridge.py +86 -0
- novacode_cli/rooms/cli.py +231 -0
- novacode_cli/rooms/nova.py +227 -0
- novacode_cli/rooms/runtime.py +299 -0
- novacode_cli/rooms/store.py +538 -0
- novacode_cli/rooms/workspace.py +94 -0
- novacode_cli/security/__init__.py +29 -0
- novacode_cli/security/approval_context.py +45 -0
- novacode_cli/security/delegated_approval.py +68 -0
- novacode_cli/security/local_http.py +85 -0
- novacode_cli/security/middleware.py +87 -0
- novacode_cli/security/paths.py +35 -0
- novacode_cli/security/policy.py +535 -0
- novacode_cli/security/policy_writer.py +90 -0
- novacode_cli/security/prompt_injection.py +203 -0
- novacode_cli/security/remember.py +81 -0
- novacode_cli/security/rule_synthesis.py +116 -0
- novacode_cli/security/secret_files.py +61 -0
- novacode_cli/security/session_allow.py +114 -0
- novacode_cli/security/unicode_security.py +516 -0
- novacode_cli/security/validator.py +173 -0
- novacode_cli/server/__init__.py +9 -0
- novacode_cli/server/app.py +632 -0
- novacode_cli/server/event_adapter.py +189 -0
- novacode_cli/server/session_manager.py +90 -0
- novacode_cli/server_main.py +64 -0
- novacode_cli/server_runner/dev_server.py +530 -0
- novacode_cli/server_runner/test_runner.py +470 -0
- novacode_cli/session/__init__.py +1 -0
- novacode_cli/session/adapters/__init__.py +240 -0
- novacode_cli/session/adapters/claude_code.py +102 -0
- novacode_cli/session/adapters/codex.py +77 -0
- novacode_cli/session/adapters/harness_native.py +75 -0
- novacode_cli/session/checkpoint_coordinator.py +448 -0
- novacode_cli/session/context_restore.py +25 -0
- novacode_cli/session/history_export.py +228 -0
- novacode_cli/session/history_tree.py +174 -0
- novacode_cli/session/imported_context.py +267 -0
- novacode_cli/session/runtime_identity.py +32 -0
- novacode_cli/session/session_persistence.py +1168 -0
- novacode_cli/session/session_prompt_builder.py +279 -0
- novacode_cli/session/session_restore.py +351 -0
- novacode_cli/session/session_summarization.py +20 -0
- novacode_cli/session/transcript_journal.py +153 -0
- novacode_cli/sessions/__init__.py +11 -0
- novacode_cli/sessions/gateway.py +84 -0
- novacode_cli/sessions/launch.py +51 -0
- novacode_cli/sessions/lease.py +213 -0
- novacode_cli/sessions/messaging.py +255 -0
- novacode_cli/sessions/protocol.py +216 -0
- novacode_cli/sessions/resume.py +31 -0
- novacode_cli/sessions/supervisor.py +498 -0
- novacode_cli/sessions/tasks.py +73 -0
- novacode_cli/sessions/tui_messaging.py +413 -0
- novacode_cli/sessions/worker.py +652 -0
- novacode_cli/sessions/worktree.py +262 -0
- novacode_cli/shell/__init__.py +27 -0
- novacode_cli/shell/jobs.py +475 -0
- novacode_cli/shell/middleware.py +1568 -0
- novacode_cli/shell/os_sandbox.py +205 -0
- novacode_cli/shell/output.py +181 -0
- novacode_cli/shell/patterns.py +275 -0
- novacode_cli/shell/processes.py +87 -0
- novacode_cli/shell/utils.py +236 -0
- novacode_cli/skills/__init__.py +26 -0
- novacode_cli/skills/builtin/handoff/SKILL.md +15 -0
- novacode_cli/skills/curation_middleware.py +69 -0
- novacode_cli/skills/executable.py +161 -0
- novacode_cli/skills/libraries.py +102 -0
- novacode_cli/skills/load.py +248 -0
- novacode_cli/skills/prune.py +215 -0
- novacode_cli/skills/refreshing_middleware.py +896 -0
- novacode_cli/skills/retrieval.py +248 -0
- novacode_cli/skills/runtime.py +118 -0
- novacode_cli/skills/schema.py +254 -0
- novacode_cli/skills/skill_creation.py +1549 -0
- novacode_cli/skills/skill_lock.py +75 -0
- novacode_cli/skills/skill_system_prompt.py +16 -0
- novacode_cli/skills/skills_prefs.py +126 -0
- novacode_cli/skills/tool_resolver.py +31 -0
- novacode_cli/skills/upstream.py +50 -0
- novacode_cli/skills/versioning.py +291 -0
- novacode_cli/states/Session.py +847 -0
- novacode_cli/states/__init__.py +31 -0
- novacode_cli/states/slices/__init__.py +17 -0
- novacode_cli/states/slices/agent_runtime.py +249 -0
- novacode_cli/states/slices/background_tasks.py +21 -0
- novacode_cli/states/slices/notifications.py +74 -0
- novacode_cli/states/slices/remote_bridge.py +56 -0
- novacode_cli/states/slices/ui_settings.py +49 -0
- novacode_cli/states/slices/wiki.py +25 -0
- novacode_cli/teammates/__init__.py +1 -0
- novacode_cli/teammates/controller.py +557 -0
- novacode_cli/teammates/graph.py +104 -0
- novacode_cli/teammates/registry.py +175 -0
- novacode_cli/teammates/service.py +254 -0
- novacode_cli/teammates/worker.py +19 -0
- novacode_cli/teams/__init__.py +1 -0
- novacode_cli/teams/adapters/__init__.py +1 -0
- novacode_cli/teams/adapters/container.py +316 -0
- novacode_cli/teams/adapters/inprocess.py +206 -0
- novacode_cli/teams/adapters/process.py +364 -0
- novacode_cli/teams/bootstrap.py +246 -0
- novacode_cli/teams/container_git.py +67 -0
- novacode_cli/teams/context.py +34 -0
- novacode_cli/teams/coordinator.py +815 -0
- novacode_cli/teams/gateway.py +200 -0
- novacode_cli/teams/host.py +64 -0
- novacode_cli/teams/legacy.py +52 -0
- novacode_cli/teams/middleware.py +176 -0
- novacode_cli/teams/models.py +104 -0
- novacode_cli/teams/output.py +47 -0
- novacode_cli/teams/protocol.py +50 -0
- novacode_cli/teams/recovery.py +80 -0
- novacode_cli/teams/scheduler.py +44 -0
- novacode_cli/teams/store.py +631 -0
- novacode_cli/teams/worker.py +251 -0
- novacode_cli/teams/workspace.py +100 -0
- novacode_cli/token_utils.py +256 -0
- novacode_cli/tools/__init__.py +104 -0
- novacode_cli/tools/_shared.py +97 -0
- novacode_cli/tools/_types.py +24 -0
- novacode_cli/tools/artifact_tools.py +137 -0
- novacode_cli/tools/code_search_tools.py +330 -0
- novacode_cli/tools/computer_tools.py +77 -0
- novacode_cli/tools/cron_tools.py +65 -0
- novacode_cli/tools/daemon_tool.py +290 -0
- novacode_cli/tools/desktop_tools.py +161 -0
- novacode_cli/tools/fetch_tools.py +530 -0
- novacode_cli/tools/http_tools.py +279 -0
- novacode_cli/tools/inspection_tools.py +107 -0
- novacode_cli/tools/job_tools.py +143 -0
- novacode_cli/tools/knowledge_tools.py +427 -0
- novacode_cli/tools/memory_tools.py +461 -0
- novacode_cli/tools/memory_wiki_tools.py +40 -0
- novacode_cli/tools/notebook_tools.py +238 -0
- novacode_cli/tools/oracle_tool.py +83 -0
- novacode_cli/tools/plan_archive_tools.py +80 -0
- novacode_cli/tools/plan_mode_tools.py +315 -0
- novacode_cli/tools/python_kernel_loop.py +165 -0
- novacode_cli/tools/python_kernel_tool.py +222 -0
- novacode_cli/tools/reflection_tools.py +34 -0
- novacode_cli/tools/scraper_tools.py +377 -0
- novacode_cli/tools/skill_tools.py +275 -0
- novacode_cli/tools/speak_tool.py +51 -0
- novacode_cli/tools/store_memory_tools.py +227 -0
- novacode_cli/tools/ui_tools.py +62 -0
- novacode_cli/tools/waiter_tools.py +408 -0
- novacode_cli/tools/web_tools.py +349 -0
- novacode_cli/tools/wiki_tools.py +152 -0
- novacode_cli/tools/worktree_tools.py +219 -0
- novacode_cli/tracking/__init__.py +10 -0
- novacode_cli/tracking/file_tracker.py +826 -0
- novacode_cli/tracking/local_metrics.py +170 -0
- novacode_cli/tracking/loop_guard.py +507 -0
- novacode_cli/tracking/request_metrics.py +60 -0
- novacode_cli/tracking/tracing.py +692 -0
- novacode_cli/tracking/usage_tree.py +677 -0
- novacode_cli/tracking/workspace_anchoring.py +231 -0
- novacode_cli/tui/__init__.py +11 -0
- novacode_cli/tui/animation_rate.py +9 -0
- novacode_cli/tui/animations.py +332 -0
- novacode_cli/tui/app.py +19315 -0
- novacode_cli/tui/auth_display.py +87 -0
- novacode_cli/tui/auth_screens.py +826 -0
- novacode_cli/tui/avatar.py +294 -0
- novacode_cli/tui/avatar_frames.py +21 -0
- novacode_cli/tui/background_tasks.py +35 -0
- novacode_cli/tui/browser_settings_screen.py +70 -0
- novacode_cli/tui/caveman_screen.py +185 -0
- novacode_cli/tui/council_settings_screen.py +182 -0
- novacode_cli/tui/council_view.py +535 -0
- novacode_cli/tui/decision_settings.py +268 -0
- novacode_cli/tui/diff_preview.py +199 -0
- novacode_cli/tui/gc_tuning.py +63 -0
- novacode_cli/tui/handoff_screen.py +206 -0
- novacode_cli/tui/harness.py +244 -0
- novacode_cli/tui/history_tree_screen.py +179 -0
- novacode_cli/tui/home.py +328 -0
- novacode_cli/tui/home_scene.py +94 -0
- novacode_cli/tui/icons.py +81 -0
- novacode_cli/tui/interface_styles.tcss +327 -0
- novacode_cli/tui/matrix3.py +108 -0
- novacode_cli/tui/mcp_tools_screen.py +131 -0
- novacode_cli/tui/motion.py +178 -0
- novacode_cli/tui/output_body.py +136 -0
- novacode_cli/tui/output_buffer.py +59 -0
- novacode_cli/tui/palette.py +334 -0
- novacode_cli/tui/pickers.py +237 -0
- novacode_cli/tui/prime_panel.py +506 -0
- novacode_cli/tui/provider_signin.py +213 -0
- novacode_cli/tui/pty_shell.py +159 -0
- novacode_cli/tui/reference_screens.py +444 -0
- novacode_cli/tui/resume_picker.py +346 -0
- novacode_cli/tui/rooms.py +597 -0
- novacode_cli/tui/screens.py +7088 -0
- novacode_cli/tui/session_import.py +333 -0
- novacode_cli/tui/session_pane.py +242 -0
- novacode_cli/tui/session_tab.py +42 -0
- novacode_cli/tui/session_transcript.py +118 -0
- novacode_cli/tui/shell_output.py +7 -0
- novacode_cli/tui/stall_watch.py +174 -0
- novacode_cli/tui/statusline.py +40 -0
- novacode_cli/tui/statusline_screen.py +91 -0
- novacode_cli/tui/teammate_updates.py +162 -0
- novacode_cli/tui/teammates.py +548 -0
- novacode_cli/tui/terminal.py +289 -0
- novacode_cli/tui/terminal_view.py +235 -0
- novacode_cli/tui/themes.py +118 -0
- novacode_cli/tui/update_notice.py +84 -0
- novacode_cli/tui/voice_downloads.py +113 -0
- novacode_cli/tui/widgets.py +2038 -0
- novacode_cli/tui/windows_input.py +38 -0
- novacode_cli/tui/workspace_approval.py +165 -0
- novacode_cli/ui/council_web.py +113 -0
- novacode_cli/ui/council_workspace.py +72 -0
- novacode_cli/ui/cowork_web.py +41 -0
- novacode_cli/ui/diff_highlight.py +160 -0
- novacode_cli/ui/execution.py +1396 -0
- novacode_cli/ui/exit_summary.py +65 -0
- novacode_cli/ui/hitl_approval.py +196 -0
- novacode_cli/ui/input_preparation.py +2 -0
- novacode_cli/ui/interrupt_handlers.py +215 -0
- novacode_cli/ui/question_prompt.py +151 -0
- novacode_cli/ui/status_phrases.py +157 -0
- novacode_cli/ui/stream_preview.py +66 -0
- novacode_cli/ui/streaming.py +2 -0
- novacode_cli/ui/subagent_tracking.py +2 -0
- novacode_cli/ui/ui_elements.py +1585 -0
- novacode_cli/ui/web_workspace.py +244 -0
- novacode_cli/ui_events.py +299 -0
- novacode_cli/update_ui.py +243 -0
- novacode_cli/updates.py +489 -0
- novacode_cli/utils/backend_patches.py +399 -0
- novacode_cli/utils/backend_paths.py +467 -0
- novacode_cli/utils/lazy_providers.py +51 -0
- novacode_cli/utils/model_info.py +223 -0
- novacode_cli/utils/pdf_extraction.py +183 -0
- novacode_cli/utils/startup.py +29 -0
- novacode_cli/utils/tasks.py +42 -0
- novacode_cli/utils/windows.py +9 -0
- novacode_cli/vixie/hook_adapter.py +127 -0
- novacode_cli/vixie/server.py +375 -0
- novacode_cli/wiki/__init__.py +15 -0
- novacode_cli/wiki/ask.py +56 -0
- novacode_cli/wiki/file.py +71 -0
- novacode_cli/wiki/ingest.py +184 -0
- novacode_cli/wiki/manager.py +261 -0
- novacode_cli-1.2.0.dist-info/METADATA +271 -0
- novacode_cli-1.2.0.dist-info/RECORD +691 -0
- novacode_cli-1.2.0.dist-info/WHEEL +5 -0
- novacode_cli-1.2.0.dist-info/entry_points.txt +2 -0
- novacode_cli-1.2.0.dist-info/licenses/LICENSE +21 -0
- novacode_cli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""The one public entry point for context-window management.
|
|
2
|
+
|
|
3
|
+
``ContextManager`` is a thin, model-aware facade over the package's private
|
|
4
|
+
modules (``_analysis``, ``_budget``, ``_model_config``, ``_dynamic``,
|
|
5
|
+
``_conversation``). Callers construct it with a model name and call its methods
|
|
6
|
+
instead of importing scattered free functions.
|
|
7
|
+
|
|
8
|
+
cm = ContextManager(model_name)
|
|
9
|
+
cm.window_size() # int
|
|
10
|
+
cm.breakdown(messages, tools=tools) # ContextBreakdown
|
|
11
|
+
cm.model_config() # ModelConfig
|
|
12
|
+
|
|
13
|
+
Model-independent operations don't require a model name:
|
|
14
|
+
|
|
15
|
+
ContextManager().budget() # ContextBudget (process-global)
|
|
16
|
+
await ContextManager().digest(agent, thread_id) # str
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
|
+
|
|
23
|
+
from novacode_cli.context._analysis import (
|
|
24
|
+
ContextBreakdown,
|
|
25
|
+
build_context_breakdown,
|
|
26
|
+
get_context_window_size,
|
|
27
|
+
)
|
|
28
|
+
from novacode_cli.context._budget import ContextBudget
|
|
29
|
+
from novacode_cli.context._conversation import get_recent_conversation_digest
|
|
30
|
+
from novacode_cli.context._model_config import ModelConfig, get_model_config
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from langchain_core.messages import BaseMessage
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ContextManager:
|
|
37
|
+
"""Model-aware facade for all context-window operations."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, model_name: str | None = None, *, use_dynamic: bool = True):
|
|
40
|
+
"""Create a context manager.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
model_name: Model to bind analysis/config operations to. May be None
|
|
44
|
+
for model-independent operations (``budget``, ``digest``).
|
|
45
|
+
use_dynamic: Whether to consult Ollama for dynamic window detection.
|
|
46
|
+
"""
|
|
47
|
+
self.model_name = model_name
|
|
48
|
+
self.use_dynamic = use_dynamic
|
|
49
|
+
self._budget = None
|
|
50
|
+
|
|
51
|
+
def _require_model(self) -> str:
|
|
52
|
+
if not self.model_name:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"ContextManager was created without a model_name; this operation requires one."
|
|
55
|
+
)
|
|
56
|
+
return self.model_name
|
|
57
|
+
|
|
58
|
+
# ── Analysis (model-bound) ──────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
def window_size(self) -> int:
|
|
61
|
+
"""Context window size in tokens for the bound model."""
|
|
62
|
+
return get_context_window_size(self._require_model(), use_dynamic=self.use_dynamic)
|
|
63
|
+
|
|
64
|
+
def breakdown(
|
|
65
|
+
self, messages: list[BaseMessage], tools: list[Any] | None = None
|
|
66
|
+
) -> ContextBreakdown:
|
|
67
|
+
"""Token breakdown of ``messages`` against the bound model's window.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
messages: Conversation messages from agent state.
|
|
71
|
+
tools: Tool definitions bound to the model, if known — their schemas
|
|
72
|
+
are a permanent per-request cost and belong in the baseline.
|
|
73
|
+
"""
|
|
74
|
+
return build_context_breakdown(
|
|
75
|
+
messages, self._require_model(), use_dynamic=self.use_dynamic, tools=tools
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def model_config(self) -> ModelConfig:
|
|
79
|
+
"""Per-model tuning config (budgets, thresholds, cost) for the bound model."""
|
|
80
|
+
return get_model_config(self._require_model(), use_dynamic=self.use_dynamic)
|
|
81
|
+
|
|
82
|
+
# ── Model-independent ───────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
def budget(self, max_tokens: int = 50000) -> ContextBudget:
|
|
85
|
+
"""The process-global middleware context-budget tracker."""
|
|
86
|
+
if self._budget is None or self._budget.max_tokens != max_tokens:
|
|
87
|
+
self._budget = ContextBudget(max_tokens)
|
|
88
|
+
return self._budget
|
|
89
|
+
|
|
90
|
+
async def digest(
|
|
91
|
+
self,
|
|
92
|
+
agent: Any,
|
|
93
|
+
thread_id: str,
|
|
94
|
+
*,
|
|
95
|
+
max_turns: int = 10,
|
|
96
|
+
max_chars: int = 4000,
|
|
97
|
+
) -> str:
|
|
98
|
+
"""Compact transcript of the most recent user/assistant turns."""
|
|
99
|
+
return await get_recent_conversation_digest(
|
|
100
|
+
agent, thread_id, max_turns=max_turns, max_chars=max_chars
|
|
101
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Request-local references for byte-identical successful file observations.
|
|
2
|
+
|
|
3
|
+
Tools always execute. Only duplicate text in the final dispatch view changes;
|
|
4
|
+
checkpoint messages, call arguments, ordering and full recovery stay intact.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from langchain_core.messages import AIMessage, ToolMessage
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from langchain_core.messages import AnyMessage
|
|
16
|
+
|
|
17
|
+
MIN_OBSERVATION_CHARS = 2_048
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def compact_observations(messages: list[AnyMessage]) -> list[AnyMessage]:
|
|
21
|
+
"""Reference an earlier identical read in this view, never a cached read.
|
|
22
|
+
|
|
23
|
+
Keys include the exact arguments and text, rather than just the file path.
|
|
24
|
+
A read after an edit must have actually returned the same bytes to qualify.
|
|
25
|
+
Sources must survive in the same request; rebuilding from original messages
|
|
26
|
+
on every dispatch prevents dangling references after compaction or resume.
|
|
27
|
+
Nontext results and failed reads are never rewritten.
|
|
28
|
+
"""
|
|
29
|
+
result = list(messages)
|
|
30
|
+
pending: dict[str, str] = {}
|
|
31
|
+
seen: dict[tuple[str, str], int] = {}
|
|
32
|
+
for index, message in enumerate(messages):
|
|
33
|
+
if isinstance(message, AIMessage):
|
|
34
|
+
for call in message.tool_calls:
|
|
35
|
+
call_id = call.get("id")
|
|
36
|
+
if call_id:
|
|
37
|
+
pending.pop(call_id, None)
|
|
38
|
+
if call["name"] == "read_file":
|
|
39
|
+
pending[call_id] = json.dumps(call["args"], sort_keys=True)
|
|
40
|
+
elif isinstance(message, ToolMessage):
|
|
41
|
+
arguments = pending.pop(message.tool_call_id, None)
|
|
42
|
+
if (
|
|
43
|
+
arguments is None
|
|
44
|
+
or message.name != "read_file"
|
|
45
|
+
or message.status == "error"
|
|
46
|
+
or not isinstance(message.content, str)
|
|
47
|
+
or len(message.content) < MIN_OBSERVATION_CHARS
|
|
48
|
+
):
|
|
49
|
+
continue
|
|
50
|
+
key = (arguments, message.content)
|
|
51
|
+
if key not in seen:
|
|
52
|
+
seen[key] = index
|
|
53
|
+
continue
|
|
54
|
+
source = seen[key] + 1
|
|
55
|
+
result[index] = message.model_copy(
|
|
56
|
+
update={
|
|
57
|
+
"content": (
|
|
58
|
+
"[Identical read_file output: this fresh read returned exactly the text "
|
|
59
|
+
f"retained in request message {source} "
|
|
60
|
+
"(counting conversation messages from 1, excluding the system prompt). "
|
|
61
|
+
"Use that full result; no new contents omitted.]"
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
return result
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""One shared policy for reacting to context pressure.
|
|
2
|
+
|
|
3
|
+
Every renderer must apply the SAME rule. It previously lived inline in
|
|
4
|
+
``tui/app.py`` only, so the console path recorded the breakdown and then did
|
|
5
|
+
nothing — no warning, no compaction, straight into the provider's limit on a
|
|
6
|
+
long session.
|
|
7
|
+
|
|
8
|
+
Kept pure (no agent, no model, no I/O) so the policy is unit-testable without a
|
|
9
|
+
running agent and cannot drift between surfaces.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from enum import Enum
|
|
16
|
+
|
|
17
|
+
from novacode_cli.context._analysis import (
|
|
18
|
+
CONTEXT_CRITICAL_THRESHOLD,
|
|
19
|
+
CONTEXT_WARNING_THRESHOLD,
|
|
20
|
+
MIN_RESERVE_TOKENS,
|
|
21
|
+
compact_threshold_pct,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PressureAction(Enum):
|
|
26
|
+
"""What the caller should do about current context usage."""
|
|
27
|
+
|
|
28
|
+
NONE = "none"
|
|
29
|
+
"""Comfortably below the warning threshold — do nothing."""
|
|
30
|
+
|
|
31
|
+
WARN = "warn"
|
|
32
|
+
"""Past the warning threshold — tell the user, suggest /compact."""
|
|
33
|
+
|
|
34
|
+
COMPACT = "compact"
|
|
35
|
+
"""Past the auto-compaction threshold — compact now."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class PressureDecision:
|
|
40
|
+
"""The decision plus the state the caller must carry forward."""
|
|
41
|
+
|
|
42
|
+
action: PressureAction
|
|
43
|
+
reason: str
|
|
44
|
+
usage_percentage: float
|
|
45
|
+
compacted_last_turn: bool
|
|
46
|
+
"""Value the caller should store as its `_compacted_last_turn` flag."""
|
|
47
|
+
|
|
48
|
+
disable_auto_compact: bool = False
|
|
49
|
+
"""Set once repeated compaction demonstrably is not freeing space.
|
|
50
|
+
|
|
51
|
+
A too-small window, or an agent that keeps re-filling context (e.g. by
|
|
52
|
+
re-reading the offloaded history to "recover" the task), makes compaction a
|
|
53
|
+
no-op — repeating it only spams the summary. The caller stops and hands
|
|
54
|
+
control back to the user instead.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def assess_pressure(
|
|
59
|
+
usage_percentage: float,
|
|
60
|
+
*,
|
|
61
|
+
compacted_last_turn: bool = False,
|
|
62
|
+
auto_compact_enabled: bool = True,
|
|
63
|
+
context_window: int = 0,
|
|
64
|
+
) -> PressureDecision:
|
|
65
|
+
"""Decide how to react to ``usage_percentage`` of the model's window.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
usage_percentage: Current usage as a percentage (0-100+).
|
|
69
|
+
compacted_last_turn: Whether the caller already auto-compacted on the
|
|
70
|
+
immediately preceding turn. If usage is still critical after that,
|
|
71
|
+
compaction is not winning and the loop is broken.
|
|
72
|
+
auto_compact_enabled: False when the caller has already disabled
|
|
73
|
+
auto-compaction for this session; it then only warns.
|
|
74
|
+
context_window: The model's window in tokens. Given, the compaction
|
|
75
|
+
point also has to leave :data:`MIN_RESERVE_TOKENS` free — see
|
|
76
|
+
:func:`compact_threshold_pct`. 0 keeps the plain percentage.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A :class:`PressureDecision`. ``action`` is ``COMPACT`` only when
|
|
80
|
+
auto-compaction is enabled AND it has not already failed to help.
|
|
81
|
+
"""
|
|
82
|
+
if usage_percentage >= compact_threshold_pct(context_window):
|
|
83
|
+
if not auto_compact_enabled:
|
|
84
|
+
return PressureDecision(
|
|
85
|
+
action=PressureAction.WARN,
|
|
86
|
+
reason=(
|
|
87
|
+
f"Context critical: {usage_percentage:.0f}% — auto-compact is disabled; run "
|
|
88
|
+
"/compact now, or /clear to start fresh."
|
|
89
|
+
),
|
|
90
|
+
usage_percentage=usage_percentage,
|
|
91
|
+
compacted_last_turn=False,
|
|
92
|
+
)
|
|
93
|
+
if compacted_last_turn:
|
|
94
|
+
# Compaction ran last turn and we are critical again → not helping.
|
|
95
|
+
return PressureDecision(
|
|
96
|
+
action=PressureAction.WARN,
|
|
97
|
+
reason=(
|
|
98
|
+
"Repeated auto-compaction isn't freeing space (likely a "
|
|
99
|
+
"context-window loop). Auto-compact disabled — use /clear to "
|
|
100
|
+
"start fresh, or switch to a larger-context model."
|
|
101
|
+
),
|
|
102
|
+
usage_percentage=usage_percentage,
|
|
103
|
+
compacted_last_turn=False,
|
|
104
|
+
disable_auto_compact=True,
|
|
105
|
+
)
|
|
106
|
+
return PressureDecision(
|
|
107
|
+
action=PressureAction.COMPACT,
|
|
108
|
+
reason=f"Context {usage_percentage:.0f}% — auto-compacting to free space…",
|
|
109
|
+
usage_percentage=usage_percentage,
|
|
110
|
+
compacted_last_turn=True,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if usage_percentage >= CONTEXT_WARNING_THRESHOLD * 100:
|
|
114
|
+
return PressureDecision(
|
|
115
|
+
action=PressureAction.WARN,
|
|
116
|
+
reason=f"Context usage high: {usage_percentage:.0f}% — consider /compact soon.",
|
|
117
|
+
usage_percentage=usage_percentage,
|
|
118
|
+
# A compaction (if any) did free space — reset the loop guard.
|
|
119
|
+
compacted_last_turn=False,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return PressureDecision(
|
|
123
|
+
action=PressureAction.NONE,
|
|
124
|
+
reason="",
|
|
125
|
+
usage_percentage=usage_percentage,
|
|
126
|
+
compacted_last_turn=False,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def post_compaction_still_critical(
|
|
131
|
+
usage_percentage: float, *, auto_compact_enabled: bool, context_window: int = 0
|
|
132
|
+
) -> PressureDecision:
|
|
133
|
+
"""Re-assess right after a compaction, to detect that it is not working.
|
|
134
|
+
|
|
135
|
+
Returns ``disable_auto_compact=True`` when usage is still at the compaction
|
|
136
|
+
threshold — the summary itself is near the window (usually a too-small
|
|
137
|
+
model), so further auto-compaction is futile.
|
|
138
|
+
"""
|
|
139
|
+
decision = assess_pressure(
|
|
140
|
+
usage_percentage,
|
|
141
|
+
compacted_last_turn=False,
|
|
142
|
+
auto_compact_enabled=auto_compact_enabled,
|
|
143
|
+
context_window=context_window,
|
|
144
|
+
)
|
|
145
|
+
if decision.action is PressureAction.COMPACT:
|
|
146
|
+
return PressureDecision(
|
|
147
|
+
action=PressureAction.WARN,
|
|
148
|
+
reason=(
|
|
149
|
+
"Auto-compact couldn't free enough space (context window too small "
|
|
150
|
+
"for this conversation). Auto-compact disabled — use /clear to start "
|
|
151
|
+
"fresh or switch to a larger-context model."
|
|
152
|
+
),
|
|
153
|
+
usage_percentage=usage_percentage,
|
|
154
|
+
compacted_last_turn=False,
|
|
155
|
+
disable_auto_compact=True,
|
|
156
|
+
)
|
|
157
|
+
return decision
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
__all__ = [
|
|
161
|
+
"CONTEXT_CRITICAL_THRESHOLD",
|
|
162
|
+
"MIN_RESERVE_TOKENS",
|
|
163
|
+
"PressureAction",
|
|
164
|
+
"PressureDecision",
|
|
165
|
+
"assess_pressure",
|
|
166
|
+
"compact_threshold_pct",
|
|
167
|
+
"post_compaction_still_critical",
|
|
168
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Supported provider preparation and content-free HTTP attempt observation."""
|
|
2
|
+
|
|
3
|
+
# JSON and LangGraph interfaces carry heterogeneous values.
|
|
4
|
+
# ruff: noqa: ANN401
|
|
5
|
+
import json
|
|
6
|
+
from contextvars import ContextVar
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class AttemptCounter:
|
|
13
|
+
"""One dispatch's real HTTP attempts; no URLs, headers, or body retained."""
|
|
14
|
+
|
|
15
|
+
count: int = 0
|
|
16
|
+
observed: bool = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
http_attempts: ContextVar[AttemptCounter | None] = ContextVar("nova_http_attempts", default=None)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _observe(request: Any) -> None:
|
|
23
|
+
del request # Never inspect headers, body, URL, or credentials.
|
|
24
|
+
counter = http_attempts.get()
|
|
25
|
+
if counter:
|
|
26
|
+
counter.count += 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def _aobserve(request: Any) -> None:
|
|
30
|
+
_observe(request)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def instrument_http(model: Any) -> bool:
|
|
34
|
+
"""Attach HTTPX hooks to supported SDK clients without changing transports."""
|
|
35
|
+
supported = False
|
|
36
|
+
for name, callback in (
|
|
37
|
+
("root_client", _observe),
|
|
38
|
+
("root_async_client", _aobserve),
|
|
39
|
+
("_client", _observe),
|
|
40
|
+
("_async_client", _aobserve),
|
|
41
|
+
):
|
|
42
|
+
sdk = getattr(model, name, None)
|
|
43
|
+
client = getattr(sdk, "_client", None)
|
|
44
|
+
hooks = getattr(client, "event_hooks", None)
|
|
45
|
+
if isinstance(hooks, dict) and "request" in hooks:
|
|
46
|
+
if callback not in hooks["request"]:
|
|
47
|
+
hooks["request"].append(callback)
|
|
48
|
+
supported = True
|
|
49
|
+
return supported
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def prepared_request(request: Any, messages: Any, schemas: Any) -> Any:
|
|
53
|
+
"""Prepare OpenAI/Anthropic payloads using their installed adapter code."""
|
|
54
|
+
model = request.model
|
|
55
|
+
# Inspect adapter ancestry before importing optional providers. Nova's
|
|
56
|
+
# gateway wrappers live in model_create but inherit the installed adapter.
|
|
57
|
+
modules = tuple(base.__module__ for base in type(model).__mro__)
|
|
58
|
+
anthropic = openai = False
|
|
59
|
+
if any(module.startswith("langchain_anthropic.") for module in modules):
|
|
60
|
+
from langchain_anthropic import ChatAnthropic
|
|
61
|
+
|
|
62
|
+
anthropic = isinstance(model, ChatAnthropic)
|
|
63
|
+
elif any(module.startswith("langchain_openai.") for module in modules):
|
|
64
|
+
from langchain_openai import ChatOpenAI
|
|
65
|
+
|
|
66
|
+
openai = isinstance(model, ChatOpenAI)
|
|
67
|
+
if not (openai or anthropic):
|
|
68
|
+
return (), None
|
|
69
|
+
prepare = getattr(model, "_get_request_payload", None)
|
|
70
|
+
if prepare is None:
|
|
71
|
+
return (), None
|
|
72
|
+
settings = dict(getattr(request, "model_settings", None) or {})
|
|
73
|
+
if schemas:
|
|
74
|
+
# The adapters' binding paths preserve built-ins and provider extras;
|
|
75
|
+
# generic schema conversion loses server search and deferred definitions.
|
|
76
|
+
settings["tools"] = model.bind_tools(request.tools).kwargs["tools"]
|
|
77
|
+
response_format = getattr(request, "response_format", None)
|
|
78
|
+
if response_format is not None:
|
|
79
|
+
from langchain.agents.structured_output import ProviderStrategy, ToolStrategy
|
|
80
|
+
|
|
81
|
+
if isinstance(response_format, ProviderStrategy) and openai:
|
|
82
|
+
settings.update(response_format.to_model_kwargs())
|
|
83
|
+
elif isinstance(response_format, ToolStrategy) and openai:
|
|
84
|
+
tools = list(settings.get("tools", []))
|
|
85
|
+
tools.extend(
|
|
86
|
+
{
|
|
87
|
+
"type": "function",
|
|
88
|
+
"function": {
|
|
89
|
+
"name": spec.name,
|
|
90
|
+
"description": spec.description or "",
|
|
91
|
+
"parameters": spec.json_schema,
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
for spec in response_format.schema_specs
|
|
95
|
+
)
|
|
96
|
+
settings["tools"] = tools
|
|
97
|
+
else:
|
|
98
|
+
return (), None
|
|
99
|
+
payload = prepare(messages, **settings)
|
|
100
|
+
encoded = json.dumps(payload, ensure_ascii=False, sort_keys=True, default=str)
|
|
101
|
+
reservation = next(
|
|
102
|
+
(
|
|
103
|
+
payload.get(key)
|
|
104
|
+
for key in ("max_output_tokens", "max_completion_tokens", "max_tokens")
|
|
105
|
+
if payload.get(key) is not None
|
|
106
|
+
),
|
|
107
|
+
0,
|
|
108
|
+
)
|
|
109
|
+
return (encoded,), int(reservation)
|