- Lower default max_agent_step from 30 to 3 across all agent runners
(coze, dashscope, deerflow, dify) for faster responses
- Refactor ExecuteShellTool to use plumbum with session-based isolation,
maintaining shell state per session (cwd, env vars, etc.)
- Remove unused API-specific environment variables from cmd_run
- Fix data access bug in shipyard_neo._maybe_model_dump
- Add plumbum>=1.10.0 dependency
- Dashboard: Catch missing index.html error, log warning, and disable WebUI instead of crashing.
- Dashboard: Use anyio.Path for async file existence checks in SSL config.
- CLI/Backup: Replace blocking file operations with anyio async operations.
- Core: Replace blocking file operations with anyio in Coze/Dify clients and TTS simulation.
- Provider: Rename 'timeout' param to 'init_timeout'/'request_timeout' to fix ASYNC109 warnings.
- Ruff: Fix various ASYNC230/ASYNC240 errors across multiple files.
Multiple models (Gemini 3, GPT-5.2, Claude Sonnet, Kimi K2.5) consistently
pick FileDownloadTool when they should pick FileUploadTool. The old
descriptions used "upload/download" which is ambiguous from the LLM's
perspective — it doesn't know which side is "local" vs "remote".
Rewrite descriptions to use explicit directional language:
- Upload: "Transfer FROM host INTO sandbox" + "when user sends a file"
- Download: "Transfer FROM sandbox OUT to host" + "ONLY when user asks
to retrieve/export"
Also improve parameter descriptions with the same directional clarity.
Fixes#6497
Co-authored-by: Yufeng He <40085740+universeplayer@users.noreply.github.com>
* Fix CreateSkillPayloadTool array schema missing items field
The payload parameter's anyOf array variant lacked an items field,
causing Gemini API to reject the tool declaration with 400 Bad Request:
'parameters.properties[payload].any_of[1].items: missing field.'
Add items: {type: object} to the array variant to satisfy the Gemini
API requirement for array type schemas.
Fixes#6279
* Fix TypeError when OpenAI-compatible API returns null choices
Some providers (e.g. OpenRouter) may return a completion where
choices is None rather than an empty list — for instance on rate
limiting, content filtering, or transient errors. The existing code
used len(completion.choices) which throws TypeError on None.
Replace all len(...choices) == 0 checks with 'not ... .choices' which
handles both None and empty list. Affects _query_stream, _parse_openai_completion,
and _extract_reasoning_content.
Fixes#6252
- .gitignore: keep both .serena and .worktrees/ entries
- astr_main_agent_resources.py: keep deletion (refactored to tools/)
- send_message.py: port video message type support from master
- shipyard_neo: browser property now returns None when not initialized
instead of raising RuntimeError, matching ComputerBooter base contract
- computer_tool_provider: remove dead os.environ writes for shipyard
(SHIPYARD_ENDPOINT / SHIPYARD_ACCESS_TOKEN are never read anywhere)
and remove unused os import
- Rewrite TestApplySandboxToolsRefactored to test ComputerToolProvider
directly (_apply_sandbox_tools was removed; old tests permanently skipped)
- Add TestExecutorCapabilityGuard with 5 strict tests for browser
capability rejection at executor level
- Fix typo: "on hehalf you" -> "on behalf of you" in subagent result
- Remove extra blank lines (ruff E303) after _apply_sandbox_tools comment
- Remove dead _build_sync_and_scan_command (no callers after refactor)
Two changes to make the tool schema sent to the LLM deterministic:
1. ToolSet.normalize() — sort tools by name before serialization.
Called at the end of build_main_agent() after all injection passes.
Eliminates ordering drift from plugin load order, MCP reconnection,
and persona tool list differences.
2. Always inject full sandbox tool set — ComputerToolProvider now
returns get_default_sandbox_tools() unconditionally, regardless of
sandbox boot state. Browser tools are always in the schema even if
the sandbox profile lacks browser capability. The executor rejects
calls to unavailable browser tools with a descriptive error instead
of silently omitting them from the schema.
This eliminates the pre-boot/post-boot tool set jump that caused
prefix cache misses on the second request of a conversation.
- Add get_default_tools/get_tools/get_system_prompt_parts to ComputerBooter base
- Each booter subclass (ShipyardNeo, Shipyard, Boxlite) declares its own tools
- ComputerToolProvider now delegates to booter API via computer_client helpers
- Add unified query API: get_sandbox_tools, get_default_sandbox_tools, etc.
- Extract Neo prompts to dedicated computer/prompts.py module
- Add booter type constants (booters/constants.py)
- Fix subagent tool path to pass sandbox_cfg and session_id
- Fix Sourcery issues: shell injection in send_message, typo in prompts,
internal tools bypass inactivated_llm_tools check
* feat: add video message support and enhance message type descriptions in SendMessageToUserTool
* feat: add error handling for disabled sandbox runtime in get_booter function
Add explicit Neo lifecycle instructions to the main agent prompt so
skill creation and updates follow payload -> candidate -> promotion
instead of direct local folder writes.
Clarify lifecycle tool descriptions and parameter semantics, including
skill_key/source_execution_ids usage and stable release sync_to_local
behavior, to reduce ambiguity and improve consistent skill publishing.
- Resolve skills root via Path.resolve() so LLM prompts always
reference absolute paths regardless of sandbox cwd
- Use resolved path in skill metadata for reliable cat/head commands
- Add DRY cross-reference comment for frontmatter parser
- Remove dead skills_root_abs field from JSON output (no consumer)
- Remove unnecessary os import and fake resolve/abspath branch