Commit Graph

1060 Commits

Author SHA1 Message Date
Administrator acd436acfe feat(agent): add session state rollback on hard abort
- 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.
2026-03-16 21:49:58 +08:00
Administrator 1236dd9e6d feat(agent): add concurrency semaphore and hard abort for SubTurn
- 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
2026-03-16 21:03:58 +08:00
Administrator ceeae15d8a feat(agent): wire SubTurn into AgentLoop and Spawn Tool
- 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
2026-03-16 20:44:04 +08:00
Dmitrii Balabanov 64ceb5ab76 fix(logger): show first/last 4 chars of bot token for identification 2026-03-16 12:48:28 +02:00
Dmitrii Balabanov 8fc36a4f9b fix(logger): mask bot tokens in 3rd-party logger output 2026-03-16 12:48:28 +02:00
Argobell 1ace296b91 fix: use fileEvent instead of event when appending fields for file logger
Co-authored-by: argobell <183611258+argobell@users.noreply.github.com>
2026-03-16 16:46:13 +08:00
xiaoen b768dab822 test(agent): use realistic session data in context retry test
Session history only stores user/assistant/tool messages — the system
prompt is built dynamically by BuildMessages. Remove the incorrect
system message from TestAgentLoop_ContextExhaustionRetry test data
to match the real data model that forceCompression operates on.
2026-03-16 14:48:35 +08:00
xiaoen 7c1a1c2c1a style(agent): fix gci comment alignment in test 2026-03-16 14:48:35 +08:00
xiaoen edbdc3bcf1 fix(agent): findSafeBoundary returns 0 for single-Turn history
When the entire history is a single Turn (one user message followed by
tool calls and responses, no subsequent user message), the only Turn
boundary is at index 0. Previously the fallback returned targetIndex,
which could land on a tool or assistant message — splitting the Turn.

Return 0 instead, so callers (forceCompression, summarizeSession) see
mid <= 0 and skip compression rather than cutting inside the Turn.
2026-03-16 14:48:35 +08:00
xiaoen 8034ee7be1 fix(agent): correct media token arithmetic and tool call double-counting
Two estimation bugs fixed:

1. Media tokens were added to the chars accumulator before the chars*2/5
   conversion, resulting in 256*2/5=102 tokens per item instead of 256.
   Fix: add media tokens directly to the final token count, bypassing
   the character-based heuristic.

2. estimateMessageTokens counted both tc.Name and tc.Function.Name for
   tool calls, but providers only send one (OpenAI-compat uses
   function.name, Anthropic uses tc.Name). Fix: count tc.Function.Name
   when Function is present, fall back to tc.Name only otherwise.

Also fix i18n hint text: "auto-detect" was misleading — the backend
uses a 4x max_tokens heuristic, not actual model detection.
2026-03-16 14:48:34 +08:00
xiaoen 639739cb85 refactor(agent): use Turn as the atomic unit for compression cut-off
Introduce parseTurnBoundaries() which identifies each Turn start index
in the session history. A Turn is a complete "user input → LLM iterations
→ final response" cycle (as defined in the agent refactor design #1316).

findSafeBoundary now uses Turn boundaries instead of raw role-scanning,
making the intent explicit: "find the nearest Turn boundary."

forceCompression drops the oldest half of Turns (not arbitrary messages),
which is simpler and more intuitive. The Turn-based approach naturally
prevents splitting tool-call sequences since each Turn is atomic.
2026-03-16 14:48:34 +08:00
xiaoen efd403242e fix(agent): preallocate messages slice in budget test
Fixes prealloc lint warning by using make() with capacity hint.
2026-03-16 14:48:34 +08:00
xiaoen b7f1c2b5fc test(agent): add realistic session-shaped tests for context budget
Add tests that reflect actual session data shape: history starts with
user messages (no system prompt), includes chained tool-call sequences,
reasoning content, and media items. Exercises the proactive budget check
path with BuildMessages-style assembled messages.
2026-03-16 14:48:34 +08:00
xiaoen d5fdd5ebd2 fix(agent): include ReasoningContent and Media in token estimation
estimateMessageTokens now counts ReasoningContent (extended thinking /
chain-of-thought) which can be substantial and is persisted in session
history. Media items get a fixed per-item overhead (256 tokens) since
actual cost depends on provider-specific image tokenization.
2026-03-16 14:48:33 +08:00
xiaoen 9c65d78b07 fix(agent): forceCompression must not assume history[0] is system prompt
Session history (GetHistory) contains only user/assistant/tool messages.
The system prompt is built dynamically by BuildMessages and is never
stored in session. The previous code incorrectly treated history[0] as
a system prompt, skipping the first user message and appending a
compression note to it.

Fix: operate on the full history slice, and record the compression
note in the session summary (which BuildMessages already injects into
the system prompt) rather than modifying any history message.
2026-03-16 14:48:33 +08:00
xiaoen 9c82b0baa2 refactor(agent): context boundary detection, proactive budget check, and safe compression
Separate context_window from max_tokens — they serve different purposes
(input capacity vs output generation limit). The previous conflation caused
premature summarization or missed compression triggers.

Changes:
- Add context_window field to AgentDefaults config (default: 4x max_tokens)
- Extract boundary-safe truncation helpers (isSafeBoundary, findSafeBoundary)
  into context_budget.go — pure functions with no AgentLoop dependency
- forceCompression: align split to safe boundary so tool-call sequences
  (assistant+ToolCalls → tool results) are never torn apart
- summarizeSession: use findSafeBoundary instead of hardcoded keep-last-4
- estimateTokens: count ToolCalls arguments and ToolCallID metadata,
  not just Content — fixes systematic undercounting in tool-heavy sessions
- Add proactive context budget check before LLM call in runAgentLoop,
  preventing 400 context-length errors instead of reacting to them
- Add estimateToolDefsTokens for tool definition token cost

Closes #556, closes #665
Ref #1439
2026-03-16 14:48:32 +08:00
Administrator ae23193295 feat(agent): port subturn PoC to refactor/agent branch
- 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>
2026-03-16 14:31:32 +08:00
sky5454 2f10b47f59 feat(credential): part1 add AES-GCM encryption, SecureStore, and onboard ke… (#1521)
* feat(credential): add AES-GCM encryption, SecureStore, and onboard keygen

- pkg/credential: new package with AES-256-GCM enc:// credential format,
  HKDF-SHA256 key derivation (passphrase + optional SSH key binding),
  ErrPassphraseRequired / ErrDecryptionFailed sentinel errors,
  and PassphraseProvider hook for runtime passphrase injection

- pkg/credential/store: lock-free SecureStore via atomic.Pointer[string];
  passphrase never written to disk or os.Environ

- pkg/credential/keygen: ed25519 SSH key generation helper used by onboard

- pkg/config: replace os.Getenv(PassphraseEnvVar) with
  credential.PassphraseProvider() at all three call sites so that
  LoadConfig and SaveConfig use whatever passphrase source is active

- cmd/picoclaw/onboard: prompt for passphrase with echo-off, generate
  picoclaw-specific SSH key, re-encrypt existing config on re-onboard

- docs/credential_encryption.md: design doc for the enc:// format

* fix(credential): address Copilot review comments on PR #1521

- credential.go: decouple ErrPassphraseRequired from env var name;
  message is now 'enc:// passphrase required' since PassphraseProvider
  may come from any source, not just os.Environ

- credential.go: Resolver resolves symlinks via EvalSymlinks before the
  isWithinDir containment check, preventing symlink-based path traversal
  for file:// credential references

- store.go: tighten comment to describe only what SecureStore guarantees
  (in-memory only); remove claims about how callers transport the value

- store_test.go: replace the meaningless GetReturnsCopy test (Go strings
  are immutable, equality across two calls proves nothing) with
  TestSecureStore_ConcurrentSetGet that exercises atomic.Pointer under
  10-goroutine concurrent Set/Get load

- config_test.go: update error-message assertion to match new sentinel text

- docs/credential_encryption.md: remove reference to non-existent
  'picoclaw encrypt' subcommand; describe the onboard flow instead

* fix(config): encryptPlaintextAPIKeys: struct-based encryption, fail-fast, remove raw []byte

* fix(credential): require SSH private key for encryption/decryption, remove passphrase-only mode

* lint: fix credential keygen lint, fix test keygen

* onboard: make encryption opt-in via --enc flag

Encryption (passphrase prompt + SSH key generation) is now only
triggered when the user passes --enc to 'picoclaw onboard'.
Without the flag, onboard skips the credential-encryption setup and
writes a plain config + workspace templates directly.

- Add --enc BoolFlag in NewOnboardCommand()
- Pass encrypt bool into onboard()
- Guard passphrase prompt, SSH key generation, and related env-var
  setup behind the encrypt branch
- Adjust 'Next steps' output so the passphrase reminder only appears
  when --enc was used
2026-03-16 14:06:32 +08:00
BitToby 71e2b636d6 fix: Use secure defaults for Pico channel setup and stop leaking the token in the URL (#1563)
* fix: Use secure defaults for Pico channel setup and stop leaking the token in the URL

* fix: Derive default allow_origins from the setup request's Origin header instead of hardcoding localhost ports
2026-03-16 09:58:37 +08:00
afjcjsbx de68688c75 fix lint 2026-03-15 22:30:02 +01:00
afjcjsbx d5c2bc538a feat(tool): markdown format in output web_fetch tool 2026-03-15 22:12:03 +01:00
Mauro 021aa7d6d5 feat(agent): steering (#1517)
* feat(agent): steering

* fix loop

* fix lint

* fix lint
2026-03-16 00:08:16 +08:00
duomi 5660b8f24b fix(heartbeat): ignore untouched default template 2026-03-15 21:58:21 +08:00
Hoshina f7dd040ae4 fix(provider/azure): lint err 2026-03-15 12:45:11 +08:00
Mauro 5a251b46af Merge pull request #1442 from afjcjsbx/feat/logger-stdout-formatting
feat(logger): Custom console formatter for JSON and multiline strings
2026-03-14 22:04:51 +01:00
Kunal Karmakar 5fb4b3bedf feat(provider): add support for azure openai provider (#1422)
* Add support for azure openai provider

* Add checks for deployment model name

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Addressing @Copilot suggestion to remove the init() function which seemed redundant

* Fix readme

* Fix linting checks

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-14 22:52:34 +08:00
Hoshina f71eaaf7f8 fix(cron): default scheduled jobs to agent execution 2026-03-14 21:03:23 +08:00
Hoshina bb1a414527 fix(tools): harden whitelist path resolution 2026-03-14 19:58:23 +08:00
dataCenter430 0c5d7500e8 feat: expose local file paths for non-image media to enable agent file tools (#1516)
* feat: expose local file paths for non-image media to enable agent file tools

* fix: Golang Lint error
2026-03-14 12:09:11 +08:00
Hoshina 345452fba8 refactor(tools): remove unused validatePath wrapper 2026-03-14 12:08:11 +08:00
Hoshina 1bc05e8392 fix(tools): allow sandbox access to temp media files 2026-03-14 12:02:06 +08:00
Hoshina b9aaad95cd refactor(media): centralize temp media dir path 2026-03-14 12:01:47 +08:00
Alix-007 c68b4f3903 fix(qq): populate account bindings metadata (#1456)
Co-authored-by: XYSK-lilong007 <267018309+XYSK-lilong007@users.noreply.github.com>
2026-03-13 23:08:55 +08:00
afjcjsbx 78c9b86d7e added tests 2026-03-13 14:02:28 +01:00
Hakancan 6b72326be1 fix: safety guard incorrectly blocks commands with URLs (#1254)
* 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.
2026-03-13 17:16:05 +08:00
lxowalle 9530883d2c Fix/Add warning tips for MCP initialization when no valid servers configured (#1497)
* add tips for mcp

* fix test issue
2026-03-13 16:43:00 +08:00
美電球 4ccea5eb93 fix(identity): prevent allowlist ID entries from matching usernames (#1406) 2026-03-13 15:41:18 +08:00
Cytown 9676e51e89 make gateway aware of config.json change (#1187)
* 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
2026-03-13 14:27:46 +08:00
Cytown dfa36f39cb add model command to set default model (#1250)
* 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
2026-03-13 14:10:11 +08:00
Zane Tung 9fed4ec136 feat: add anthropic-messages protocol for native Anthropic Messages API support Fixes #269 (#1284)
* 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>
2026-03-13 14:09:40 +08:00
leamon 0fb92b21b6 enhance skill installer (#1252)
* enhance skill installer

* enhance install skills v2

* go file formate

* fix:use proxy download skills;many chunck download;simple code

* add default config to config.example.json, download skill from github use proxy and token

---------

Co-authored-by: FantasticCode2019 <1443996278@qq.com>
2026-03-13 14:04:02 +08:00
dataCenter430 b811e9186c feat(provider): add ModelScope as OpenAI-compatible provider (#1486)
* feat(provider): add ModelScope as OpenAI-compatible provider

* test(provider): add ModelScope provider and migration tests

* docs: add ModelScope to README provider tables and free tier sections

* chore: add ModelScope to example config and env template
2026-03-13 14:02:23 +08:00
Cytown 83e24e8ceb fix 3rd party logger not correct output (#1482) 2026-03-13 11:20:17 +08:00
Eric Jacksch 56fb0dc4e3 fix(claude_cli): surface stdout in error when CLI exits non-zero
When the claude CLI exits with a non-zero status, the previous error
handler only checked stderr. However, the CLI writes its output
(including error details) to stdout, especially when invoked with
--output-format json. This left the caller with only "exit status 1"
and no actionable information.

Now includes both stderr and stdout in the error message so the actual
failure reason is visible in logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:01:56 -04:00
afjcjsbx a01af36af4 feat(logger): add custom console formatter for JSON and multiline strings 2026-03-12 18:58:24 +01:00
don 19835b2f60 fix(line): limit webhook request body size to prevent DoS (#1413)
* 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>
2026-03-12 23:55:40 +08:00
Horsley Lee 8f49af99f9 fix(matrix): stream inbound media downloads to disk (#1436) 2026-03-12 23:48:26 +08:00
Guoguo 1e024321c0 refactor: update model name and add VolcEngine coding plan (#1412)
* 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>
2026-03-12 16:10:29 +08:00
Alix-007 3bcbfd99b9 fix(channels): stop stale typing loops on overwrite (#1392)
Co-authored-by: XYSK-lilong007 <267018309+XYSK-lilong007@users.noreply.github.com>
2026-03-12 14:31:00 +08:00
Cytown 7359b2c86c add testcase for migrate from v0 to v1 2026-03-12 14:09:31 +08:00