Critical fixes (5):
- Fix turnState hierarchy corruption in nested SubTurns by checking context
before creating new root turnState in runAgentLoop
- Fix deadlock risk in deliverSubTurnResult by separating lock and channel ops
- Fix session rollback race in HardAbort by calling Finish() before rollback
- Fix resource leak by closing pendingResults channel in Finish() with recovery
- Add thread-safety docs for childTurnIDs and isFinished fields
Medium priority fixes (5):
- Move globalTurnCounter to AgentLoop.subTurnCounter to prevent ID conflicts
- Improve semaphore acquisition to ensure release even on early validation failures
- Document design choice: ephemeral sessions start empty for complete isolation
- Add final poll before Finish() to capture late-arriving SubTurn results
- Remove duplicate channel registration in spawnSubTurn to fix timing issues
Testing:
- Add 6 new tests covering hierarchy, deadlock, ordering, channel lifecycle,
final poll, and semaphore behavior
- All 12 SubTurn tests passing with race detector
This resolves 10 critical and medium issues (5 race conditions, 2 resource leaks,
3 timing issues) identified in code review, bringing SubTurn to production-ready state.
- Fix turnState hierarchy corruption when SubTurns recursively call runAgentLoop
by checking context for existing turnState before creating new root
- Fix deadlock risk in deliverSubTurnResult by separating lock and channel operations
- Fix session rollback race in HardAbort by calling Finish() before rollback
- Fix resource leak by closing pendingResults channel in Finish() with panic recovery
- Add thread-safety documentation for childTurnIDs and isFinished fields
- Move globalTurnCounter to AgentLoop.subTurnCounter to prevent ID conflicts
- Improve semaphore acquisition to ensure release even on early validation failures
- Document design choice: ephemeral sessions start empty for complete isolation
- Add 5 new tests: hierarchy, deadlock, order, channel close, and semaphore
- Add initialHistoryLength field to turnState to snapshot session state at turn start
- Save initial history length in runAgentLoop when creating root turnState
- Implement session rollback in HardAbort via SetHistory, truncating to initial length
- Add TestHardAbortSessionRollback to verify history rollback after abort
- Import providers package in subturn_test.go for Message type
This ensures that when a user triggers hard abort, all messages added during
the aborted turn are discarded, restoring the session to its pre-turn state.
- Add maxConcurrentSubTurns constant (5) and concurrencySem channel to turnState
- Acquire/release semaphore in spawnSubTurn to limit concurrent child turns per parent
- Add activeTurnStates sync.Map to AgentLoop for tracking root turn states by session
- Implement HardAbort(sessionKey) method to trigger cascading cancellation via turnState.Finish()
- Register/unregister root turnState in runAgentLoop for hard abort lookup
- Add TestSubTurnConcurrencySemaphore to verify semaphore capacity enforcement
- Add TestHardAbortCascading to verify context cancellation propagates to child turns
- Add subTurnResults sync.Map to AgentLoop for per-session channel tracking
- Add register/unregister/dequeue methods in steering.go
- Poll SubTurn results in runLLMIteration at loop start and after each tool,
injecting results as [SubTurn Result] messages into parent conversation
- Initialize root turnState in runAgentLoop, propagate via context
(withTurnState/turnStateFromContext), call rootTS.Finish() on completion
- Wire Spawn Tool to spawnSubTurn via SetSpawner in registerSharedTools,
recovering parentTS from context for proper turn hierarchy
- Refactor subagent.go to use SetSpawner pattern
- Add TestSubTurnResultChannelRegistration and TestDequeuePendingSubTurnResults
- Replace duplicate types (ToolResult/Session/Message) with real project types
- Implement ephemeralSessionStore satisfying session.SessionStore interface
- Connect runTurn to real AgentLoop via runAgentLoop + AgentInstance
- Fix subturn_test.go to match updated signatures and types
Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
- centralize Pico chat connection and session state in a shared store
- move chat lifecycle control out of usePicoChat
- hydrate and restore the active session across the app
- add a dedicated /api/gateway/logs endpoint for incremental log polling
- keep /api/gateway/status focused on runtime and health data only
- update frontend log fetching to use the new API and add backend tests covering the status/logs separation and cleared-log behavior
* fix: safety guard incorrectly blocks commands with URLs
The absolutePathPattern regex was matching URL path components like
//github.com as file system paths, causing commands containing URLs
to be incorrectly blocked by the workspace restriction safety guard.
For example, 'agent-browser open https://github.com' would be blocked
because //github.com was treated as an absolute file path outside
the working directory.
The fix adds a check to skip any path match that starts with '//',
as these are URL path components, not file system paths.
Fixes#1203
* fix: handle file:// URIs correctly in safety guard
The previous fix skipped all paths starting with '//', which incorrectly
also skipped file:// URIs that could escape the workspace sandbox.
Changes:
- Only skip '//' paths when preceded by web URL schemes (http:, https:, ftp:, etc.)
- file:// URIs are now properly checked against workspace boundaries
- Added TestShellTool_FileURISandboxing to verify the fix
Fixes security issue raised by @alexhoshina in PR #1254
* style: fix gofumpt formatting
* fix(safety-guard): use exact match position to prevent URL exemption bypass
Using strings.Index(cmd, raw) always returned the first occurrence of the
matched substring, allowing a bypass where the same //path appeared both
inside a URL and as a standalone shell path (e.g. echo https://etc/passwd
&& cat //etc/passwd would skip the second match).
Switch to FindAllStringIndex so each match is evaluated at its actual
position in the command string.
Adds TestShellTool_URLBypassPrevented to cover the exploit scenario.
- track boot and config default models in gateway status/events
- preserve running, starting, and restarting states during health checks
- add safer gateway restart handling with stronger backend test coverage
- expose restart-required UI and refresh model state after default model update
* make gateway aware of config.json change
* fix according to code review
* fix lint
* fix review comment
* fix for review
* refactor to fix review
* fix for review
* fix for review
* add model command to set default model
* fix for ci
* fix test for model
* fix active agent not recognized
* implement test for model command
* fix local-model can not set as default issue
* fix review comment
* fix for comment
* feat: add anthropic-messages protocol support
Add native Anthropic Messages API format support to enable
compatibility with custom endpoints that only support Anthropic's
native message format (not OpenAI-compatible format).
Changes:
- Add new pkg/providers/anthropic_messages package with HTTP-based provider
- Implement Anthropic Messages API request/response format conversion
- Add anthropic-messages protocol support in factory_provider.go
- Include comprehensive unit tests (64.2% coverage)
Features:
- Support for system, user, assistant, and tool messages
- Support for tool calls (tool_use blocks)
- Proper header handling (x-api-key, anthropic-version)
- Configurable max_tokens and temperature
- Automatic base URL normalization
Configuration example:
model: "anthropic-messages/claude-opus-4-6"
api_base: "https://api.anthropic.com"
api_key: "sk-..."
Tested with actual API endpoint, verified compatibility
with Anthropic Messages API specification.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add anthropic-messages protocol examples to README and config
Add configuration examples and documentation for the new
anthropic-messages protocol:
- config.example.json: Add claude-opus-4.6 example with anthropic-messages
- README.md: Add "Anthropic Messages API (native format)" section
- README.zh.md: Add Chinese version of the documentation
This helps users understand when to use anthropic-messages vs
anthropic protocol and fixes issue #269.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: format code with gofmt -s
- Align constant definitions in provider.go
- Align struct fields in test cases
- Fix gofmt formatting issues reported in review
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address linter errors
- Fix HTTP header canonical form: "x-api-key" → "X-API-Key"
- Fix HTTP header canonical form: "anthropic-version" → "Anthropic-Version"
- Format imports with gci (standard, default, localmodule order)
- Format code with golines (max line length 120)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: resolve golangci-lint errors in anthropic-messages provider
- add nolint comment for canonicalheader rule on X-API-Key header (Anthropic API requires exact casing)
- fix golines formatting issues in provider_test.go (split long lines under 120 chars)
- fix long comment line in factory_provider.go (split into two lines)
Resolves CI linter failures for the anthropic-messages protocol implementation.
* fix(providers): address review comments in anthropic-messages provider
- fix normalizeBaseURL edge case that incorrectly appends /v1 to URLs already containing /v1 path (e.g., https://api.example.com/v1/proxy)
- remove dead code for apiBase empty check as normalizeBaseURL() always provides a default value
- update test to use proper constructor instead of direct struct initialization
- add detailed comments explaining the URL normalization logic
Resolves review comments on PR #1284
* fix(providers): remove hardcoded max_tokens in anthropic-messages provider
- remove hardcoded max_tokens value (4096) from buildRequestBody
- read max_tokens directly from options parameter
- add error handling when max_tokens is missing from options
- update test cases to include max_tokens in options
This fix ensures the provider respects the config default value (32768)
or system fallback (8192) instead of always using the hardcoded 4096.
* fix(providers): improve error handling and add edge case tests
- fix ToolCalls nil vs empty slice issue to ensure consistent JSON serialization
- add detailed HTTP error handling for common status codes (401, 429, 400, 404, 500, 503)
- add edge case tests for buildRequestBody and parseResponseBody
- clarify anthropic vs anthropic-messages protocol differences in docs
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(line): limit webhook request body size to prevent DoS
Add io.LimitReader with 1 MB cap on the LINE webhook handler to prevent
unauthenticated memory exhaustion via oversized POST requests.
Follows the same pattern used in the WeCom channel (io.LimitReader).
Requests exceeding the limit are rejected with 413 Request Entity Too Large.
Fixes#1407
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(line): hoist body size const, add boundary tests
- Move maxWebhookBodySize to package-level const
- Add TestWebhookAcceptsMaxBodySize (exact limit → 403, not 413)
- Add TestWebhookRejectsOversizedBodyBeforeSignatureCheck
- Use const in test instead of magic number
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Go's built-in mime.TypeByExtension returns 'image/svg' for .svg files,
but the correct MIME type per RFC 6838 is 'image/svg+xml'. This fix
registers the correct MIME type when setting up the static file server.
Fixes#1410
- Disable goreleaser GitHub release for nightly (Docker still pushed)
- Use GORELEASER_CURRENT_TAG with local-only tag for version/validation
- Force-update single `nightly` git tag instead of creating per-day tags
- Docker tags use only `nightly`/`nightly-launcher`, no per-day versions
- Set --latest=false on nightly release to avoid occupying latest
- Simplify workflow from 3 jobs to 1 job, remove all cleanup steps
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: swap header logo to webp, move meme logo to bottom
Replace header logo with assets/logo.webp across all 6 README
language variants and move the original meme logo (logo.jpg)
to the bottom of each file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update GPT model names to gpt-5.4 and refine provider descriptions
Update all 6 language README variants:
- Correct GPT model references from gpt-5.2/gpt4 to gpt-5.4
- Refine provider descriptions in API Key comparison tables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update default model to gpt-5.4, codex to gpt-5.3-codex
Update OpenAI default model references from gpt-5.2 to gpt-5.4
across source code, config examples, tests, and docs. Set Codex
default model to gpt-5.3-codex.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
TOOLS.md was intentionally removed in 21d60f6 and #771, as tools are
now provided to the LLM via JSON schema through ToProviderDefs().
These references were missed during that cleanup.
Suggested by @yinwm in #1355.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>