#3150 reworded write_file's description, overwrite parameter, and guard
error to prefer append_file/edit_file. But those tools register via their
own enable flags and the per-agent allowlist, so a config exposing only
write_file was steered toward tools it does not have — a dead end in the
most restrictive setups.
WriteFileTool now tracks which non-destructive alternatives are available
and names only the ones present; when none are, the guard still blocks the
overwrite without pointing anywhere. The agent wiring registers the editors
before write_file and resolves availability via registry.HasRegistered,
which reflects both the enable flag and the allowlist.
Tests: conditional copy at the tool level (none/one/both) and agent wiring
across the enable-flag and allowlist dimensions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The overwrite guard error ("Set overwrite=true to replace") steered the
model to clobber files like MEMORY.md. Reword write_file's description,
overwrite param, and guard error to prefer append_file/edit_file.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the Brave Search API returns HTTP 200 with zero results, log a
warning with the response body preview to help diagnose silent failures.
This addresses cases where the API response format has changed or a
non-standard error response is misinterpreted as a successful empty
result, leaving the LLM with a misleading "No results" message.
Previously, empty results were silently returned to the LLM as "No
results for: <query>", making it impossible to distinguish between
genuinely empty results and API format mismatches or silent errors.
Closes#3125
Allow allowlisted remote channels to list, get, and update their own
command cron jobs while preserving exact channel/chat ownership checks.
Apply the same access control to remove, enable, and disable so remote
callers cannot mutate jobs outside their channel/chat scope. Keep wildcard
remote allowlist behavior constrained by exact job ownership.
Add regression tests for allowlisted, non-allowlisted, wildcard, internal,
and non-command reminder access paths.
Add tools.cron.command_allowed_remotes so remote channels can be
explicitly allowlisted for command-executing cron jobs. Entries support
channel names, channel:chat_id pairs, and a literal * to allow every
non-empty channel while preserving the default deny posture.
The existing allow_command and command_confirm checks still apply after the
channel gate. Update tests, config examples, and cron documentation for the
new option.
Add defer-recover to 11 goroutines across 4 files to prevent
ungoroutine panics from crashing the entire process:
- pkg/tools/toolloop.go: parallel tool execution
- pkg/channels/manager.go: HTTP server (x2), channel registration
- pkg/events/subscription.go: concurrent dispatch, timeout handler,
watchContext
- pkg/tools/shell.go: cmd.Wait, PTY cmd.Wait, PTY read, pipe read
Key design decisions:
- Recover handlers send fallback values to channels (shell done,
subscription done) to prevent deadlocks when the producer panics
- PTY cmd.Wait sets session.Status='error' on panic for consistency
- toolloop sets ErrorResult on panic so the LLM gets a meaningful
response instead of a nil result
- subscription.go uses log.Printf to match existing invokeHandler style
- Other files use project logger (ErrorCF) with stack traces
Refs: FIX-PLAN-0.3.0 #2
RFC 2544 benchmark addresses (198.18.0.0/15) are not globally routable
but were missing from the isPrivateOrRestrictedIP blocklist, allowing
SSRF bypasses via literal IPv4.
Fixes#3077
Replace raw log.Printf and fmt.Printf calls in pkg/state, pkg/agent, and pkg/tools with structured logger calls (WarnCF/InfoCF). This ensures warnings and info messages are routed through the configured logging infrastructure instead of raw stderr/stdout.
The workspace guard's absolutePathPattern regex matches /Beijing?T in commands like 'curl wttr.in/Beijing'. Since 'wttr.in' is not a recognized web scheme, the path was routed through workspace sandbox validation, which could block legitimate scheme-less URL usage (curl allows bare domains without http://).
Add detection for domain-like tokens preceding /path matches:
- looksLikeDomain: checks for dot-separated tokens that don't end with common file extensions (.py, .go, .exe, etc.)
- localPathExists: verifies the token does not exist as a local filesystem entry
This dual guard prevents the symlink bypass identified in PR #2965 review: if 'foo.bar' exists as a local symlink or directory, the path still undergoes full workspace validation.
Fixes#1042.
Replace 7 instances of ignored json.Marshal errors with proper error handling. Previously, if marshaling an ExecResponse failed, a nil byte slice would be silently converted to an empty string in the LLM response. Now each site returns ErrorResult with the marshal error message.
The Stop() method previously used a select/default pattern which was not
safe under concurrent calls — two goroutines could both pass the check
and attempt to close the same channel, causing a panic.
Replace with sync.Once to guarantee exactly-once close semantics,
matching the documented contract of being safe for concurrent use.
Review feedback: afjcjsbx
The SessionManager's background cleanup goroutine previously had no
shutdown mechanism. Each call to NewSessionManager() started a ticker
goroutine that ran indefinitely. In tests, where multiple
SessionManagers are created, this caused goroutine leaks.
This commit adds a Stop() method that cleanly shuts down the background
cleanup goroutine via a channel. Stop() is safe to call multiple times.
All existing tests now call t.Cleanup(sm.Stop) to ensure cleanup.