fix: forward thinking param in stream mode and harden stream timeout#44
fix: forward thinking param in stream mode and harden stream timeout#44
Conversation
When yolo_mode is enabled (via config.yaml `agent.yolo_mode: true` or env var `SPOON_BOT_YOLO_MODE=true`), the agent works directly in the configured workspace path instead of a sandboxed directory. All shell commands, file reads/writes, and workspace FS operations target the user's real filesystem. Changes: - config.py: add yolo_mode field to AgentLoopConfig & SpoonBotSettings; skip mkdir + write-test in YOLO mode (require existing directory) - loop.py: pass yolo_mode through AgentLoop and create_agent; extend filesystem read paths in YOLO mode - context.py: add YOLO mode banner to system prompt - workspace_fs.py: add yolo-aware path resolution to WorkspaceFSService - handler.py: propagate yolo_mode from agent to WorkspaceFSService - server.py: read SPOON_BOT_YOLO_MODE from env/config and pass to agent - channels/config.py: support yolo_mode in load_agent_config env map - Dockerfile: add SPOON_BOT_YOLO_MODE env var with usage docs - config.example.yaml, .env.example: document yolo_mode option Made-with: Cursor
13 test cases covering: - AgentLoopConfig yolo_mode defaults and validation - validate_agent_loop_params with yolo_mode (CWD default, explicit workspace) - WorkspaceFSService YOLO path resolution (relative, absolute, boundary) - WorkspaceFSService YOLO read/write operations - ContextBuilder YOLO mode banner presence/absence Made-with: Cursor
- stream()._run_and_signal() now passes thinking=True to agent.run() so the LLM actually produces thinking chunks (was silently dropped) - Increase stream deadline from hardcoded 120s to 300s/600s (thinking) and emit a STREAM_TIMEOUT error event instead of silently breaking - ConnectionManager.send_message retries 2x with backoff before disconnecting, preventing transient failures from killing sessions Made-with: Cursor
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa0c9127a6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if not resolved.get(field): | ||
| for var in env_vars: | ||
| val = os.environ.get(var) | ||
| if val: | ||
| if field in {"max_iterations", "shell_timeout", "max_output", "context_window"}: | ||
| if field in _int_fields: |
There was a problem hiding this comment.
Honor explicit
false yolo_mode settings
load_agent_config() documents YAML > env, but if not resolved.get(field) treats a configured boolean false as if the field were unset. If an operator puts agent.yolo_mode: false in YAML on a host that still has SPOON_BOT_YOLO_MODE=true, this block flips it back to true (and _lifespan() re-applies the same or at spoon_bot/gateway/server.py:95-98), so the agent can come up unsandboxed even though the config explicitly disabled YOLO mode.
Useful? React with 👍 / 👎.
| "workspace": ["SPOON_BOT_WORKSPACE_PATH"], | ||
| "max_iterations": ["SPOON_BOT_MAX_ITERATIONS", "SPOON_MAX_STEPS"], | ||
| "enable_skills": ["SPOON_BOT_ENABLE_SKILLS"], | ||
| "yolo_mode": ["SPOON_BOT_YOLO_MODE"], |
There was a problem hiding this comment.
Thread
yolo_mode into the CLI launch paths
This makes SPOON_BOT_YOLO_MODE look like a supported top-level agent setting, but both CLI entrypoints still drop it: _run_agent() builds create_kwargs without yolo_mode at spoon_bot/cli.py:534-558, and _run_gateway() does the same at spoon_bot/cli.py:998-1030. Anyone enabling YOLO via .env or config.yaml in the normal spoon-bot commands will silently stay in sandbox mode.
Useful? React with 👍 / 👎.
| if self.yolo_mode: | ||
| _extra_read.extend(p for p in self.workspace.parents if p != Path.home()) | ||
| self.tools.register(ReadFileTool(workspace=self.workspace, additional_read_paths=_extra_read, max_output=15000)) |
There was a problem hiding this comment.
Don't rely on
additional_read_paths for YOLO reads yet
In YOLO mode this tries to grant read access to the workspace parents, but ReadFileTool.execute() still calls validate_read_path(path, workspace=self._workspace) in spoon_bot/agent/tools/filesystem.py:99, which creates a fresh PathValidator without additional_read_paths. The result is that reads outside the workspace root still fail with "outside workspace boundary", so the new parent-directory navigation path is unreachable.
Useful? React with 👍 / 👎.
| if self.yolo_mode: | ||
| if not self.workspace.is_dir(): | ||
| raise ValueError( | ||
| f"YOLO mode workspace path does not exist: {self.workspace}" | ||
| ) |
There was a problem hiding this comment.
Keep the writability check for YOLO workspaces
Returning early here lets a read-only YOLO workspace pass validation, but agent startup still writes under that directory unconditionally: MemoryStore creates memory/ and MEMORY.md (spoon_bot/memory/store.py:28-37) and file-session fallback creates sessions/ (spoon_bot/session/manager.py:143-145). On a read-only bind mount the failure moves from a clear config error to a later initialization crash.
Useful? React with 👍 / 👎.
Summary
stream()._run_and_signal()was callingagent.run()without forwardingthinking=True, so the LLM never produced thinking chunks in streaming mode. Now correctly passes the param when the agent supports it.STREAM_TIMEOUTerror event emitted to the client. (2)ConnectionManager.send_messagedisconnected on the first failure; now retries 2x with backoff before disconnecting.Changes
spoon_bot/agent/loop.pythinkingto_run_and_signal(), increase stream timeout, emit error on deadlinespoon_bot/gateway/websocket/manager.pysend_message(2 retries + backoff)tests/test_stream_thinking_fix.pyTest plan
test_stream_thinking_fix.py)test_gateway_ws_bugs.py)test_streaming_thinking.py)thinking: true, stream: trueand confirm thinking chunks appearMade with Cursor