deepagents-code 0.1.28__tar.gz → 0.1.29__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.
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/CHANGELOG.md +13 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/COMMANDS.md +2 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/PKG-INFO +2 -2
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_build_info.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_env_vars.py +12 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_server_config.py +22 -10
- deepagents_code-0.1.29/deepagents_code/_testing_models.py +317 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_version.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/agent.py +90 -17
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/app.py +517 -38
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/app.tcss +5 -2
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/command_registry.py +12 -4
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config.py +37 -10
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config_manifest.py +9 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/main.py +2 -2
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/non_interactive.py +27 -12
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server.py +28 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server_manager.py +6 -4
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/textual_adapter.py +15 -9
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/ui.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/chat_input.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/model_selector.py +2 -2
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/status.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/pyproject.toml +2 -2
- deepagents_code-0.1.29/tests/integration_tests/test_auto_approve_remote.py +198 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent.py +71 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_app.py +926 -27
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_command_registry.py +24 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_selector.py +24 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_rubric_cli.py +15 -7
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server.py +103 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_config.py +3 -3
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_graph.py +1 -1
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_textual_adapter.py +97 -22
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/uv.lock +1 -1
- deepagents_code-0.1.28/deepagents_code/_testing_models.py +0 -144
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/.gitignore +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/AGENTS.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/ARCHITECTURE.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/DEVELOPMENT.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/Makefile +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/README.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/THREAT_MODEL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/__main__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_ask_user_types.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_cli_context.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_constants.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_debug.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_git.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_paths.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_session_stats.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_startup_error.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/_textual_patches.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/approval_mode.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/ask_user.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_display.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/auth_store.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/clipboard.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/config_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/configurable_model.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/default_agent_prompt.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/doctor.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/editor.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/event_bus.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/extras_info.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/file_ops.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/formatting.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/goal_rubric.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/goal_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/hooks.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/input.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/openai_codex.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_config.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_factory.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_provider.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/integrations/sandbox_registry.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/local_context.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/managed_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_auth.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_disabled.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_login_service.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/_registry.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/base.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/github.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_providers/slack.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/mcp_trust.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/media_utils.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/memory_guard.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/model_config.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/notifications.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/offload.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/onboarding.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/output.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/project_utils.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/py.typed +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/remote_client.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/resume_state.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/server_graph.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/sessions.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/invocation.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/skills/load.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/state_migration.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/subagents.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/system_prompt.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/terminal_capabilities.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/terminal_escape.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/theme.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tool_display.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/tools_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/unicode_security.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/update_check.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/_js_eval_display.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/_links.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/agent_selector.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/approval.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/ask_user.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/auth.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/autocomplete.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/codex_auth.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/cwd_switch.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/diff.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/goal_review.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/history.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/install_confirm.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/launch_init.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/loading.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_login.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_reconnect.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/mcp_viewer.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/message_store.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/messages.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_center.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_detail.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/notification_settings.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/restart_prompt.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/subagent_panel.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/theme_selector.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/thread_selector.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/tool_renderers.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/tool_widgets.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_available.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_confirm.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/update_progress.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/deepagents_code/widgets/welcome.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/hatch_build.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/images/tui.png +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/check_imports.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/debug_server.sh +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/generate_commands_catalog.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/scripts/install.sh +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/README.md +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/conftest.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_compact_resume.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/conftest.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/data/coding_agent_v1_validator.json +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_agent_selector.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_approval_mode.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_args.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_display.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_store.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_auth_widgets.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_chat_input.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_clipboard.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_coding_agent_metadata.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_config.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_config_manifest.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_cursor_blink.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_cwd_switch.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_doctor.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_event_bus.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_extras_info.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_formatting.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_git.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_review.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_rubric.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_goal_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_hatch_build.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_history.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_command.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_confirm.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_install_script.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_launch_init.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_links.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_loading.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_managed_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_auth.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_disabled.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_login_modal.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_login_service.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_reconnect.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_memory_guard.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_message_store.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notification_center.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notification_detail.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_notifications.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_offload_dict_messages.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_onboarding.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_openai_codex_integration.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_output.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_paths.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_reload.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_restart_prompt.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_resume_state.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_config.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_factory.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_provider.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sandbox_registry.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_session_stats.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_skill_invocation.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_startup_fast_paths.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_state_migration.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_status.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagent_panel.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagent_stream.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_capabilities.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_escape.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_textual_patches.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_theme.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tool_display.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tool_renderers.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tools_commands.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_tracing_replicas.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_available.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_check.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_confirm.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_update_progress.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_version.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/test_welcome.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/test_current_thread.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/tools/test_fetch_url.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/widgets/__init__.py +0 -0
- {deepagents_code-0.1.28 → deepagents_code-0.1.29}/tests/unit_tests/widgets/test_diff.py +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
# Deep Agents Code Changelog
|
|
4
4
|
|
|
5
|
+
## [0.1.29](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.28...deepagents-code==0.1.29) (2026-07-01)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* Add rubric iteration controls ([#4405](https://github.com/langchain-ai/deepagents/issues/4405)) ([d6692a7](https://github.com/langchain-ai/deepagents/commit/d6692a7c713490f170b17510d613e02ee37574ab))
|
|
10
|
+
* Add Claude Sonnet 5 to model picker ([#4386](https://github.com/langchain-ai/deepagents/issues/4386)) ([64758dd](https://github.com/langchain-ai/deepagents/commit/64758dddf8b46c5b68c345fb1eac1fb7bcbf7f7e))
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* Full-width chat messages, hide scrollbar, flush input bg ([#4374](https://github.com/langchain-ai/deepagents/issues/4374)) ([1f8e8dc](https://github.com/langchain-ai/deepagents/commit/1f8e8dc942ac54c4014ac48f9345ee0201be0b1b))
|
|
15
|
+
* Resolve editable SDK version metadata ([#4394](https://github.com/langchain-ai/deepagents/issues/4394)) ([3239bf4](https://github.com/langchain-ai/deepagents/commit/3239bf4edf2be3037bb18315463ac6a0c3537e5c))
|
|
16
|
+
* Unblock MCP force reconnect modal ([#4396](https://github.com/langchain-ai/deepagents/issues/4396)) ([8b7eab0](https://github.com/langchain-ai/deepagents/commit/8b7eab023db686f577d16f4f9ac872205baf136f))
|
|
17
|
+
|
|
5
18
|
## [0.1.28](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.27...deepagents-code==0.1.28) (2026-06-29)
|
|
6
19
|
|
|
7
20
|
### Features
|
|
@@ -8,7 +8,7 @@ Regenerate this file with `make commands-catalog` after changing command names,
|
|
|
8
8
|
aliases, descriptions, visibility, or hidden-command metadata.
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
## Public (
|
|
11
|
+
## Public (31)
|
|
12
12
|
|
|
13
13
|
| Command | Aliases | Description |
|
|
14
14
|
| --- | --- | --- |
|
|
@@ -34,6 +34,7 @@ aliases, descriptions, visibility, or hidden-command metadata.
|
|
|
34
34
|
| `/remember` | | Save useful context to memory or skills |
|
|
35
35
|
| `/restart` | | Restart the agent server |
|
|
36
36
|
| `/rubric` | `/criteria` | Set explicit acceptance criteria for rubric grading |
|
|
37
|
+
| `/scrollbar` | | Show or hide the chat scrollbar |
|
|
37
38
|
| `/skill-creator` | | Create or refine agent skills |
|
|
38
39
|
| `/theme` | | Change color theme |
|
|
39
40
|
| `/threads` | | Browse and resume past threads |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.29
|
|
4
4
|
Summary: Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities.
|
|
5
5
|
Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
|
|
6
6
|
Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
|
|
@@ -27,7 +27,7 @@ Classifier: Topic :: Terminals
|
|
|
27
27
|
Requires-Python: <4.0,>=3.11
|
|
28
28
|
Requires-Dist: aiosqlite<1.0.0,>=0.22.1
|
|
29
29
|
Requires-Dist: deepagents-acp<1.0.0,>=0.0.8
|
|
30
|
-
Requires-Dist: deepagents==0.7.
|
|
30
|
+
Requires-Dist: deepagents==0.7.0a3
|
|
31
31
|
Requires-Dist: httpx<1.0.0,>=0.28.1
|
|
32
32
|
Requires-Dist: langchain-anthropic<2.0.0,>=1.4.7
|
|
33
33
|
Requires-Dist: langchain-google-genai<5.0.0,>=4.2.6
|
|
@@ -206,6 +206,18 @@ Defaults to enabled; set to a falsy value (`0`, `false`, `no`, `off`, or empty)
|
|
|
206
206
|
to hide replica tracing details from the splash while leaving tracing active.
|
|
207
207
|
"""
|
|
208
208
|
|
|
209
|
+
SHOW_SCROLLBAR = "DEEPAGENTS_CODE_SHOW_SCROLLBAR"
|
|
210
|
+
"""Show the vertical scrollbar in the chat area when enabled.
|
|
211
|
+
|
|
212
|
+
Off by default; use the `/scrollbar` slash command or `[ui].show_scrollbar` in
|
|
213
|
+
config.toml to toggle. Parsed by `classify_env_bool` (an unrecognized or empty
|
|
214
|
+
value falls through to the config value rather than forcing the default).
|
|
215
|
+
|
|
216
|
+
When set, this env var takes precedence over the persisted `[ui].show_scrollbar`
|
|
217
|
+
config value on launch, so a `/scrollbar` toggle will not appear to "stick"
|
|
218
|
+
across restarts while the env var remains set.
|
|
219
|
+
"""
|
|
220
|
+
|
|
209
221
|
THEME = "DEEPAGENTS_CODE_THEME"
|
|
210
222
|
"""Force the CLI to launch with this theme name when set."""
|
|
211
223
|
|
|
@@ -80,7 +80,7 @@ def _read_env_str(suffix: str) -> str | None:
|
|
|
80
80
|
return os.environ.get(f"{SERVER_ENV_PREFIX}{suffix}")
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def _read_env_int(suffix: str, *, default: int) -> int:
|
|
83
|
+
def _read_env_int(suffix: str, *, default: int | None) -> int | None:
|
|
84
84
|
"""Read a `DEEPAGENTS_CODE_SERVER_*` integer from the environment.
|
|
85
85
|
|
|
86
86
|
Args:
|
|
@@ -88,7 +88,7 @@ def _read_env_int(suffix: str, *, default: int) -> int:
|
|
|
88
88
|
default: Value when the variable is absent or malformed.
|
|
89
89
|
|
|
90
90
|
Returns:
|
|
91
|
-
Parsed integer, or the default when parsing fails.
|
|
91
|
+
Parsed integer, or the default when absent or parsing fails.
|
|
92
92
|
"""
|
|
93
93
|
raw = os.environ.get(f"{SERVER_ENV_PREFIX}{suffix}")
|
|
94
94
|
if raw is None:
|
|
@@ -264,9 +264,8 @@ class ServerConfig:
|
|
|
264
264
|
`None` reuses the main agent model.
|
|
265
265
|
"""
|
|
266
266
|
|
|
267
|
-
rubric_max_iterations: int =
|
|
268
|
-
"""
|
|
269
|
-
`'max_iterations_reached'`."""
|
|
267
|
+
rubric_max_iterations: int | None = None
|
|
268
|
+
"""Explicit grader iterations per rubric attempt; `None` uses the SDK default."""
|
|
270
269
|
|
|
271
270
|
sandbox_type: str | None = None
|
|
272
271
|
"""Sandbox backend identifier (e.g. `'daytona'`); `None` runs tools on the
|
|
@@ -304,13 +303,21 @@ class ServerConfig:
|
|
|
304
303
|
"""Normalize fields and validate invariants.
|
|
305
304
|
|
|
306
305
|
Raises:
|
|
307
|
-
|
|
306
|
+
TypeError: If `rubric_max_iterations` is a boolean.
|
|
307
|
+
ValueError: If `shell_allow_list` is an empty list or
|
|
308
|
+
`rubric_max_iterations` is non-positive.
|
|
308
309
|
"""
|
|
309
310
|
if self.sandbox_type == "none":
|
|
310
311
|
object.__setattr__(self, "sandbox_type", None)
|
|
311
312
|
if self.shell_allow_list is not None and len(self.shell_allow_list) == 0:
|
|
312
313
|
msg = "shell_allow_list must be None or non-empty"
|
|
313
314
|
raise ValueError(msg)
|
|
315
|
+
if isinstance(self.rubric_max_iterations, bool):
|
|
316
|
+
msg = "rubric_max_iterations must be None or a positive integer"
|
|
317
|
+
raise TypeError(msg)
|
|
318
|
+
if self.rubric_max_iterations is not None and self.rubric_max_iterations <= 0:
|
|
319
|
+
msg = "rubric_max_iterations must be None or a positive integer"
|
|
320
|
+
raise ValueError(msg)
|
|
314
321
|
|
|
315
322
|
# ------------------------------------------------------------------
|
|
316
323
|
# Serialization
|
|
@@ -356,7 +363,11 @@ class ServerConfig:
|
|
|
356
363
|
self.interpreter_ptc_acknowledge_unsafe
|
|
357
364
|
).lower(),
|
|
358
365
|
"RUBRIC_MODEL": self.rubric_model,
|
|
359
|
-
"RUBRIC_MAX_ITERATIONS":
|
|
366
|
+
"RUBRIC_MAX_ITERATIONS": (
|
|
367
|
+
str(self.rubric_max_iterations)
|
|
368
|
+
if self.rubric_max_iterations is not None
|
|
369
|
+
else None
|
|
370
|
+
),
|
|
360
371
|
"SANDBOX_TYPE": self.sandbox_type,
|
|
361
372
|
"SANDBOX_ID": self.sandbox_id,
|
|
362
373
|
"SANDBOX_SNAPSHOT_NAME": self.sandbox_snapshot_name,
|
|
@@ -406,7 +417,7 @@ class ServerConfig:
|
|
|
406
417
|
"INTERPRETER_PTC_ACKNOWLEDGE_UNSAFE"
|
|
407
418
|
),
|
|
408
419
|
rubric_model=_read_env_str("RUBRIC_MODEL") or None,
|
|
409
|
-
rubric_max_iterations=_read_env_int("RUBRIC_MAX_ITERATIONS", default=
|
|
420
|
+
rubric_max_iterations=_read_env_int("RUBRIC_MAX_ITERATIONS", default=None),
|
|
410
421
|
sandbox_type=_read_env_str("SANDBOX_TYPE"),
|
|
411
422
|
sandbox_id=_read_env_str("SANDBOX_ID"),
|
|
412
423
|
sandbox_snapshot_name=_read_env_str("SANDBOX_SNAPSHOT_NAME") or None,
|
|
@@ -443,7 +454,7 @@ class ServerConfig:
|
|
|
443
454
|
interpreter_ptc: str | list[str] | None = None,
|
|
444
455
|
interpreter_ptc_acknowledge_unsafe: bool = False,
|
|
445
456
|
rubric_model: str | None = None,
|
|
446
|
-
rubric_max_iterations: int =
|
|
457
|
+
rubric_max_iterations: int | None = None,
|
|
447
458
|
mcp_config_path: str | None,
|
|
448
459
|
no_mcp: bool,
|
|
449
460
|
trust_project_mcp: bool | None,
|
|
@@ -478,7 +489,8 @@ class ServerConfig:
|
|
|
478
489
|
interpreter_ptc_acknowledge_unsafe: Mirror of
|
|
479
490
|
`settings.interpreter_ptc_acknowledge_unsafe`.
|
|
480
491
|
rubric_model: Grader model spec; `None` reuses the main model.
|
|
481
|
-
rubric_max_iterations:
|
|
492
|
+
rubric_max_iterations: Explicit grader iterations per rubric attempt;
|
|
493
|
+
`None` uses the SDK default.
|
|
482
494
|
mcp_config_path: Path to MCP config.
|
|
483
495
|
no_mcp: Disable MCP.
|
|
484
496
|
trust_project_mcp: Trust project MCP servers.
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Internal chat models used by local integration tests."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
|
|
8
|
+
from langchain_core.messages import AIMessage, BaseMessage
|
|
9
|
+
from langchain_core.outputs import ChatGeneration, ChatResult
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Callable, Sequence
|
|
14
|
+
|
|
15
|
+
from langchain_core.callbacks import CallbackManagerForLLMRun
|
|
16
|
+
from langchain_core.language_models import LanguageModelInput
|
|
17
|
+
from langchain_core.runnables import Runnable
|
|
18
|
+
from langchain_core.tools import BaseTool
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Prompt markers that drive `ToolCallingIntegrationChatModel`. Each marker is the
|
|
22
|
+
# full token (including the trailing `=`); the file path follows on the same line,
|
|
23
|
+
# e.g. `DCA_TEST_WRITE_FILE=/tmp/out.txt`. These are the single source of truth
|
|
24
|
+
# shared with the integration tests, so the model and tests cannot drift apart.
|
|
25
|
+
DCA_TEST_WRITE_FILE_MARKER = "DCA_TEST_WRITE_FILE="
|
|
26
|
+
DCA_TEST_DELEGATE_WRITE_MARKER = "DCA_TEST_DELEGATE_WRITE="
|
|
27
|
+
DCA_SUBAGENT_WRITE_FILE_MARKER = "DCA_SUBAGENT_WRITE_FILE="
|
|
28
|
+
|
|
29
|
+
# Distinct file contents per write path, so a test asserting on file content can
|
|
30
|
+
# prove which branch executed — in particular that subagent mode delegated through
|
|
31
|
+
# the `task` tool rather than writing directly.
|
|
32
|
+
TOP_LEVEL_WRITE_CONTENT = "auto-approved"
|
|
33
|
+
SUBAGENT_WRITE_CONTENT = "auto-approved-subagent"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DeterministicIntegrationChatModel(GenericFakeChatModel):
|
|
37
|
+
"""Deterministic chat model for integration tests.
|
|
38
|
+
|
|
39
|
+
This subclasses LangChain's `GenericFakeChatModel` so the implementation
|
|
40
|
+
stays aligned with the core fake-chat-model test surface, while overriding
|
|
41
|
+
generation to remain prompt-driven and restart-safe for real CLI server
|
|
42
|
+
integration tests.
|
|
43
|
+
|
|
44
|
+
Why the existing `langchain_core` fakes cannot be reused here:
|
|
45
|
+
|
|
46
|
+
1. Every core fake (`GenericFakeChatModel`, `FakeListChatModel`,
|
|
47
|
+
`FakeMessagesListChatModel`) pops from an iterator or cycles an index —
|
|
48
|
+
the actual prompt is ignored. App integration tests start and stop the
|
|
49
|
+
server process, which resets in-memory state. An iterator-based model
|
|
50
|
+
either raises `StopIteration` or replays from the beginning after a
|
|
51
|
+
restart, producing wrong or missing responses. This model derives output
|
|
52
|
+
solely from the prompt text, so identical input always produces
|
|
53
|
+
identical output regardless of process lifecycle.
|
|
54
|
+
|
|
55
|
+
2. The agent runtime calls `model.bind_tools(schemas)` during
|
|
56
|
+
initialization. None of the core fakes implement `bind_tools`, so they
|
|
57
|
+
raise `AttributeError` in any agent-loop context. This model provides a
|
|
58
|
+
no-op passthrough.
|
|
59
|
+
|
|
60
|
+
3. The app server reads `model.profile` for capability negotiation (e.g.
|
|
61
|
+
`tool_calling`, `max_input_tokens`). Core fakes have no such attribute,
|
|
62
|
+
causing `AttributeError` or silent misconfiguration at runtime.
|
|
63
|
+
|
|
64
|
+
Additionally, the compact middleware issues summarization prompts mid-
|
|
65
|
+
conversation. A list-based model cannot distinguish these from normal user
|
|
66
|
+
turns without pre-knowledge of exact call ordering, whereas this model
|
|
67
|
+
detects summary requests by inspecting the prompt content.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
model: str = "fake"
|
|
71
|
+
# Required by `GenericFakeChatModel`, but our override does not consume it.
|
|
72
|
+
messages: object = Field(default_factory=lambda: iter(()))
|
|
73
|
+
profile: dict[str, Any] | None = Field(
|
|
74
|
+
default_factory=lambda: {
|
|
75
|
+
"tool_calling": True,
|
|
76
|
+
"max_input_tokens": 8000,
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def bind_tools(
|
|
81
|
+
self,
|
|
82
|
+
tools: Sequence[dict[str, Any] | type | Callable | BaseTool], # noqa: ARG002
|
|
83
|
+
*,
|
|
84
|
+
tool_choice: str | None = None, # noqa: ARG002
|
|
85
|
+
**kwargs: Any, # noqa: ARG002
|
|
86
|
+
) -> Runnable[LanguageModelInput, AIMessage]:
|
|
87
|
+
"""Return self so the agent can bind tool schemas during tests."""
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
def _generate(
|
|
91
|
+
self,
|
|
92
|
+
messages: list[BaseMessage],
|
|
93
|
+
stop: list[str] | None = None, # noqa: ARG002
|
|
94
|
+
run_manager: CallbackManagerForLLMRun | None = None, # noqa: ARG002
|
|
95
|
+
**kwargs: Any, # noqa: ARG002
|
|
96
|
+
) -> ChatResult:
|
|
97
|
+
"""Produce a deterministic reply derived from the prompt text.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
A single-message `ChatResult` with deterministic content.
|
|
101
|
+
"""
|
|
102
|
+
prompt = "\n".join(
|
|
103
|
+
text
|
|
104
|
+
for message in messages
|
|
105
|
+
if (text := self._stringify_message(message)).strip()
|
|
106
|
+
)
|
|
107
|
+
if self._looks_like_summary_request(prompt):
|
|
108
|
+
content = "integration summary"
|
|
109
|
+
else:
|
|
110
|
+
excerpt = " ".join(prompt.split()[-18:])
|
|
111
|
+
if excerpt:
|
|
112
|
+
content = f"integration reply: {excerpt}"
|
|
113
|
+
else:
|
|
114
|
+
content = "integration reply"
|
|
115
|
+
|
|
116
|
+
return ChatResult(
|
|
117
|
+
generations=[ChatGeneration(message=AIMessage(content=content))]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def _llm_type(self) -> str:
|
|
122
|
+
"""LangChain model type identifier."""
|
|
123
|
+
return "deterministic-integration"
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def _stringify_message(message: BaseMessage) -> str:
|
|
127
|
+
"""Flatten message content into plain text for deterministic responses.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Plain-text content extracted from the message.
|
|
131
|
+
"""
|
|
132
|
+
content = message.content
|
|
133
|
+
if isinstance(content, str):
|
|
134
|
+
return content
|
|
135
|
+
if isinstance(content, list):
|
|
136
|
+
parts: list[str] = []
|
|
137
|
+
for block in content:
|
|
138
|
+
if isinstance(block, str):
|
|
139
|
+
parts.append(block)
|
|
140
|
+
elif isinstance(block, dict) and block.get("type") == "text":
|
|
141
|
+
text = block.get("text")
|
|
142
|
+
if isinstance(text, str):
|
|
143
|
+
parts.append(text)
|
|
144
|
+
return " ".join(parts)
|
|
145
|
+
return str(content)
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def _looks_like_summary_request(prompt: str) -> bool:
|
|
149
|
+
"""Detect the middleware's summary-generation prompt.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
`True` when the prompt appears to be a summarization request.
|
|
153
|
+
"""
|
|
154
|
+
lowered = prompt.lower()
|
|
155
|
+
return (
|
|
156
|
+
"messages to summarize" in lowered
|
|
157
|
+
or "condense the following conversation" in lowered
|
|
158
|
+
or "<summary>" in lowered
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _extract_marker_path(prompt: str, marker: str) -> str:
|
|
163
|
+
"""Extract the file path that follows a prompt marker on the same line.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
prompt: The flattened prompt text.
|
|
167
|
+
marker: The marker token (including its trailing `=`) to locate.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The stripped file path immediately following the marker.
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
ValueError: If the marker is present but not followed by a path, so a
|
|
174
|
+
malformed test prompt fails loudly here instead of silently
|
|
175
|
+
degrading to a `"done"` reply or raising an opaque `IndexError`.
|
|
176
|
+
"""
|
|
177
|
+
_, _, tail = prompt.partition(marker)
|
|
178
|
+
lines = tail.splitlines()
|
|
179
|
+
file_path = lines[0].strip() if lines else ""
|
|
180
|
+
if not file_path:
|
|
181
|
+
msg = (
|
|
182
|
+
f"Test model saw marker {marker!r} but found no file path after it; "
|
|
183
|
+
f"check the integration-test prompt construction."
|
|
184
|
+
)
|
|
185
|
+
raise ValueError(msg)
|
|
186
|
+
return file_path
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _tool_call_result(name: str, args: dict[str, Any], call_id: str) -> ChatResult:
|
|
190
|
+
"""Build a single-tool-call `ChatResult`.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
A `ChatResult` wrapping an `AIMessage` with exactly one tool call.
|
|
194
|
+
"""
|
|
195
|
+
return ChatResult(
|
|
196
|
+
generations=[
|
|
197
|
+
ChatGeneration(
|
|
198
|
+
message=AIMessage(
|
|
199
|
+
content="",
|
|
200
|
+
tool_calls=[
|
|
201
|
+
{
|
|
202
|
+
"name": name,
|
|
203
|
+
"args": args,
|
|
204
|
+
"id": call_id,
|
|
205
|
+
"type": "tool_call",
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
]
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class ToolCallingIntegrationChatModel(DeterministicIntegrationChatModel):
|
|
215
|
+
"""Deterministic tool-calling model for auto-approve integration tests.
|
|
216
|
+
|
|
217
|
+
Generation is driven entirely by prompt markers (the module-level `DCA_*`
|
|
218
|
+
constants), so output is restart-safe and independent of call ordering — the
|
|
219
|
+
same rationale as the parent `DeterministicIntegrationChatModel`:
|
|
220
|
+
|
|
221
|
+
- `DCA_TEST_WRITE_FILE=<path>` emits a top-level `write_file` call.
|
|
222
|
+
- `DCA_TEST_DELEGATE_WRITE=<path>` emits a `task` call delegating to the
|
|
223
|
+
`general-purpose` subagent, whose prompt then carries
|
|
224
|
+
`DCA_SUBAGENT_WRITE_FILE=<path>` to trigger the subagent's `write_file`.
|
|
225
|
+
- `DCA_SUBAGENT_WRITE_FILE=<path>` emits the subagent's `write_file` call.
|
|
226
|
+
|
|
227
|
+
Each marker fires only on the agent's first turn (no prior `ToolMessage`),
|
|
228
|
+
so once the tool result returns the model replies with a plain `"done"` and
|
|
229
|
+
the agent loop terminates instead of re-issuing the tool call.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
# Only `_generate` is overridden; the inherited `_stream` would bypass this
|
|
233
|
+
# marker dispatch entirely, so streaming must be disabled.
|
|
234
|
+
disable_streaming: bool = True
|
|
235
|
+
|
|
236
|
+
def _generate(
|
|
237
|
+
self,
|
|
238
|
+
messages: list[BaseMessage],
|
|
239
|
+
stop: list[str] | None = None, # noqa: ARG002
|
|
240
|
+
run_manager: CallbackManagerForLLMRun | None = None, # noqa: ARG002
|
|
241
|
+
**kwargs: Any, # noqa: ARG002
|
|
242
|
+
) -> ChatResult:
|
|
243
|
+
"""Emit a deterministic tool call (or terminal reply) from prompt markers.
|
|
244
|
+
|
|
245
|
+
The `has_tool_result` guard ensures each marker fires only on the
|
|
246
|
+
agent's first turn; once a `ToolMessage` is present the model returns
|
|
247
|
+
`"done"` so the agent loop terminates.
|
|
248
|
+
|
|
249
|
+
A recognized marker with no file path raises `ValueError` (via
|
|
250
|
+
`_extract_marker_path`) rather than degrading to `"done"`.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
A single-message `ChatResult`: an `AIMessage` carrying a `task` or
|
|
254
|
+
`write_file` tool call when a marker matches and no tool result is
|
|
255
|
+
present yet, otherwise a plain `"done"` reply.
|
|
256
|
+
"""
|
|
257
|
+
prompt = "\n".join(
|
|
258
|
+
text
|
|
259
|
+
for message in messages
|
|
260
|
+
if (text := self._stringify_message(message)).strip()
|
|
261
|
+
)
|
|
262
|
+
has_tool_result = any(message.type == "tool" for message in messages)
|
|
263
|
+
if not has_tool_result:
|
|
264
|
+
for marker, build_tool_call in self._marker_dispatch():
|
|
265
|
+
if marker in prompt:
|
|
266
|
+
name, args, call_id = build_tool_call(
|
|
267
|
+
_extract_marker_path(prompt, marker)
|
|
268
|
+
)
|
|
269
|
+
return _tool_call_result(name, args, call_id)
|
|
270
|
+
|
|
271
|
+
return ChatResult(
|
|
272
|
+
generations=[ChatGeneration(message=AIMessage(content="done"))]
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _marker_dispatch() -> tuple[
|
|
277
|
+
tuple[str, Callable[[str], tuple[str, dict[str, Any], str]]], ...
|
|
278
|
+
]:
|
|
279
|
+
"""Return ordered `(marker, tool-call builder)` pairs.
|
|
280
|
+
|
|
281
|
+
The delegate marker is checked before the plain write markers because
|
|
282
|
+
its emitted `task` description embeds `DCA_SUBAGENT_WRITE_FILE=`; the
|
|
283
|
+
first marker found in the prompt wins.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
Marker-to-builder pairs in precedence order. Each builder maps an
|
|
287
|
+
extracted file path to a `(tool_name, args, call_id)` triple.
|
|
288
|
+
"""
|
|
289
|
+
return (
|
|
290
|
+
(
|
|
291
|
+
DCA_TEST_DELEGATE_WRITE_MARKER,
|
|
292
|
+
lambda path: (
|
|
293
|
+
"task",
|
|
294
|
+
{
|
|
295
|
+
"description": f"{DCA_SUBAGENT_WRITE_FILE_MARKER}{path}",
|
|
296
|
+
"subagent_type": "general-purpose",
|
|
297
|
+
},
|
|
298
|
+
"call_task",
|
|
299
|
+
),
|
|
300
|
+
),
|
|
301
|
+
(
|
|
302
|
+
DCA_SUBAGENT_WRITE_FILE_MARKER,
|
|
303
|
+
lambda path: (
|
|
304
|
+
"write_file",
|
|
305
|
+
{"file_path": path, "content": SUBAGENT_WRITE_CONTENT},
|
|
306
|
+
"call_write_file",
|
|
307
|
+
),
|
|
308
|
+
),
|
|
309
|
+
(
|
|
310
|
+
DCA_TEST_WRITE_FILE_MARKER,
|
|
311
|
+
lambda path: (
|
|
312
|
+
"write_file",
|
|
313
|
+
{"file_path": path, "content": TOP_LEVEL_WRITE_CONTENT},
|
|
314
|
+
"call_write_file",
|
|
315
|
+
),
|
|
316
|
+
),
|
|
317
|
+
)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Keep the `x-release-please-version` annotation — release-please uses it to
|
|
4
4
|
# bump `__version__` in sync with `pyproject.toml` on every release PR.
|
|
5
|
-
__version__ = "0.1.
|
|
5
|
+
__version__ = "0.1.29" # x-release-please-version
|
|
6
6
|
|
|
7
7
|
DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/code"
|
|
8
8
|
"""URL for `deepagents-code` documentation."""
|
|
@@ -10,13 +10,19 @@ import shutil
|
|
|
10
10
|
import tempfile
|
|
11
11
|
import tomllib
|
|
12
12
|
import warnings
|
|
13
|
-
from pathlib import Path
|
|
13
|
+
from pathlib import Path, PurePosixPath
|
|
14
14
|
from typing import TYPE_CHECKING, Any, cast
|
|
15
15
|
|
|
16
16
|
from deepagents import create_deep_agent
|
|
17
17
|
from deepagents.backends import CompositeBackend, LocalShellBackend
|
|
18
18
|
from deepagents.backends.filesystem import FilesystemBackend
|
|
19
|
-
from deepagents.middleware import
|
|
19
|
+
from deepagents.middleware import (
|
|
20
|
+
GRADER_SYSTEM_PROMPT,
|
|
21
|
+
FilesystemMiddleware,
|
|
22
|
+
MemoryMiddleware,
|
|
23
|
+
RubricMiddleware,
|
|
24
|
+
SkillsMiddleware,
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
# Backwards-compat flag: SDKs before 0.5.4 accept only `list[str]` for
|
|
22
28
|
# `SkillsMiddleware.sources`; newer SDKs expose the `SkillSource` alias
|
|
@@ -52,6 +58,10 @@ if TYPE_CHECKING:
|
|
|
52
58
|
from deepagents_code.output import OutputFormat
|
|
53
59
|
|
|
54
60
|
from langchain.agents.middleware.types import AgentMiddleware
|
|
61
|
+
from langchain.tools import (
|
|
62
|
+
ToolRuntime, # noqa: TC002 # LangChain inspects this annotation for runtime injection.
|
|
63
|
+
)
|
|
64
|
+
from langchain_core.tools import StructuredTool, tool
|
|
55
65
|
|
|
56
66
|
from deepagents_code import theme
|
|
57
67
|
from deepagents_code._cli_context import CLIContextSchema
|
|
@@ -90,6 +100,66 @@ logger = logging.getLogger(__name__)
|
|
|
90
100
|
REQUIRE_COMPACT_TOOL_APPROVAL: bool = True
|
|
91
101
|
"""When `True`, `compact_conversation` requires HITL approval like other gated tools."""
|
|
92
102
|
|
|
103
|
+
_RUBRIC_GRADER_READ_FILE_PREFIX = "/large_tool_results/"
|
|
104
|
+
_RUBRIC_GRADER_SYSTEM_PROMPT = (
|
|
105
|
+
GRADER_SYSTEM_PROMPT
|
|
106
|
+
+ "\n\nWhen the transcript says a tool result was saved under "
|
|
107
|
+
+ f"`{_RUBRIC_GRADER_READ_FILE_PREFIX}`, use the `read_file` tool to inspect "
|
|
108
|
+
+ "the referenced evidence before deciding that a criterion lacks support. "
|
|
109
|
+
+ "Only read paths that are explicitly present in the transcript."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _validate_rubric_grader_read_path(file_path: str) -> str | None:
|
|
114
|
+
normalized = file_path.replace("\\", "/")
|
|
115
|
+
if not normalized.startswith(_RUBRIC_GRADER_READ_FILE_PREFIX):
|
|
116
|
+
return "Rubric grader can only read files under /large_tool_results/."
|
|
117
|
+
parts = PurePosixPath(normalized).parts
|
|
118
|
+
if ".." in parts or "~" in parts:
|
|
119
|
+
return "Invalid path."
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _create_rubric_grader_tools(backend: CompositeBackend) -> list[BaseTool]:
|
|
124
|
+
filesystem = FilesystemMiddleware(backend=backend)
|
|
125
|
+
sdk_read_file: StructuredTool | None = None
|
|
126
|
+
for candidate in filesystem.tools:
|
|
127
|
+
if candidate.name == "read_file":
|
|
128
|
+
sdk_read_file = cast("StructuredTool", candidate)
|
|
129
|
+
break
|
|
130
|
+
if sdk_read_file is None:
|
|
131
|
+
msg = "SDK read_file tool is unavailable."
|
|
132
|
+
raise RuntimeError(msg)
|
|
133
|
+
|
|
134
|
+
sdk_read_file_func = sdk_read_file.func
|
|
135
|
+
if sdk_read_file_func is None:
|
|
136
|
+
msg = "SDK read_file tool is missing a sync implementation."
|
|
137
|
+
raise RuntimeError(msg)
|
|
138
|
+
|
|
139
|
+
@tool(description=sdk_read_file.description)
|
|
140
|
+
def read_file(
|
|
141
|
+
file_path: str,
|
|
142
|
+
runtime: ToolRuntime[None, Any],
|
|
143
|
+
offset: int = 0,
|
|
144
|
+
limit: int = 100,
|
|
145
|
+
) -> object:
|
|
146
|
+
"""Read an offloaded tool result referenced in the transcript.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
The SDK `read_file` tool result, or an error message when the path is
|
|
150
|
+
outside the grader evidence directory.
|
|
151
|
+
"""
|
|
152
|
+
if error := _validate_rubric_grader_read_path(file_path):
|
|
153
|
+
return error
|
|
154
|
+
return sdk_read_file_func(
|
|
155
|
+
file_path=file_path,
|
|
156
|
+
runtime=runtime,
|
|
157
|
+
offset=offset,
|
|
158
|
+
limit=limit,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return [read_file]
|
|
162
|
+
|
|
93
163
|
|
|
94
164
|
def _sanitize_agent_message_name(agent_name: str) -> str:
|
|
95
165
|
"""Return a provider-safe message name for a user-facing agent name.
|
|
@@ -275,17 +345,17 @@ def _resolve_ptc_option(
|
|
|
275
345
|
return None
|
|
276
346
|
|
|
277
347
|
live_names: list[str] = []
|
|
278
|
-
for
|
|
279
|
-
if isinstance(
|
|
280
|
-
name =
|
|
348
|
+
for candidate in tools:
|
|
349
|
+
if isinstance(candidate, _BaseTool):
|
|
350
|
+
name = candidate.name
|
|
281
351
|
if isinstance(name, str):
|
|
282
352
|
live_names.append(name)
|
|
283
|
-
elif isinstance(
|
|
284
|
-
raw_name = cast("dict[str, Any]",
|
|
353
|
+
elif isinstance(candidate, dict):
|
|
354
|
+
raw_name = cast("dict[str, Any]", candidate).get("name")
|
|
285
355
|
if isinstance(raw_name, str):
|
|
286
356
|
live_names.append(raw_name)
|
|
287
357
|
else:
|
|
288
|
-
attr = getattr(
|
|
358
|
+
attr = getattr(candidate, "name", None)
|
|
289
359
|
if isinstance(attr, str):
|
|
290
360
|
live_names.append(attr)
|
|
291
361
|
live_set: set[str] = set(live_names)
|
|
@@ -1251,7 +1321,7 @@ def create_cli_agent(
|
|
|
1251
1321
|
enable_shell: bool = True,
|
|
1252
1322
|
enable_interpreter: bool = False,
|
|
1253
1323
|
rubric_model: str | BaseChatModel | None = None,
|
|
1254
|
-
rubric_max_iterations: int =
|
|
1324
|
+
rubric_max_iterations: int | None = None,
|
|
1255
1325
|
checkpointer: BaseCheckpointSaver | None = None,
|
|
1256
1326
|
mcp_server_info: list[MCPServerInfo] | None = None,
|
|
1257
1327
|
cwd: str | Path | None = None,
|
|
@@ -1333,8 +1403,9 @@ def create_cli_agent(
|
|
|
1333
1403
|
A `'provider:model'` string or `BaseChatModel`.
|
|
1334
1404
|
|
|
1335
1405
|
When `None`, the main `model` is reused.
|
|
1336
|
-
rubric_max_iterations:
|
|
1337
|
-
agent terminates with `'max_iterations_reached'
|
|
1406
|
+
rubric_max_iterations: Explicit grader iterations per rubric attempt
|
|
1407
|
+
before the agent terminates with `'max_iterations_reached'`; `None`
|
|
1408
|
+
uses the SDK default.
|
|
1338
1409
|
checkpointer: Optional checkpointer for session persistence.
|
|
1339
1410
|
When `None`, the graph is compiled without a checkpointer.
|
|
1340
1411
|
mcp_server_info: MCP server metadata to surface in the system prompt.
|
|
@@ -1728,12 +1799,14 @@ def create_cli_agent(
|
|
|
1728
1799
|
message="The middleware `RubricMiddleware` is in beta",
|
|
1729
1800
|
category=Warning,
|
|
1730
1801
|
)
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1802
|
+
rubric_kwargs: dict[str, Any] = {
|
|
1803
|
+
"model": rubric_model if rubric_model is not None else model,
|
|
1804
|
+
"system_prompt": _RUBRIC_GRADER_SYSTEM_PROMPT,
|
|
1805
|
+
"tools": _create_rubric_grader_tools(composite_backend),
|
|
1806
|
+
}
|
|
1807
|
+
if rubric_max_iterations is not None:
|
|
1808
|
+
rubric_kwargs["max_iterations"] = rubric_max_iterations
|
|
1809
|
+
agent_middleware.append(RubricMiddleware(**rubric_kwargs))
|
|
1737
1810
|
|
|
1738
1811
|
# Create the agent
|
|
1739
1812
|
all_subagents: list[SubAgent | CompiledSubAgent | AsyncSubAgent] = [
|