Commit Graph

1703 Commits

Author SHA1 Message Date
lc6464 bd13092831 fix(review): align tool feedback reconstruction with runtime behavior 2026-04-09 23:52:02 +08:00
lc6464 9982ee29a8 fix(pico): avoid duplicate final websocket message 2026-04-09 22:59:36 +08:00
lc6464 2aeed8fb3a fix(pico): stream assistant text between tool calls 2026-04-09 22:32:35 +08:00
lc6464 5b596ed2f0 fix(chat): keep tool summaries and assistant output together 2026-04-09 22:16:36 +08:00
Mauro 20d3522069 Merge pull request #2418 from lahuman/docs/korean-readme
docs: add Korean README translation
2026-04-09 11:25:15 +02:00
Guoguo 5e44a99410 fix(docker): run self-built images as root for parity with release (#2435)
The self-built docker/Dockerfile and docker/Dockerfile.heavy created a
dedicated picoclaw user (uid 1000) and stored config at
/home/picoclaw/.picoclaw, while the released images from
Dockerfile.goreleaser (and Dockerfile.full) run as root at
/root/.picoclaw. Both docker-compose files mount ./data:/root/.picoclaw,
so self-built images silently broke when used with the shared compose.

Drop the picoclaw user switch and align both Dockerfiles on root +
/root/.picoclaw. Dockerfile also adopts the release entrypoint.sh so
first-run behavior matches between self-built and release tags.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:53:52 +08:00
wenjie a9720daa45 fix(test): skip TestPrepareCommand_AppliesUserEnv on unsupported operating systems (#2434) 2026-04-09 10:14:08 +08:00
k a2f02e4b18 Revert "test(agent): remove unused respondWithMediaHook field"
This reverts commit 087e355885.
2026-04-09 07:47:42 +09:00
sky5454 06023c79fa feat(launcher): standard HTTP login/setup/logout flow for dashboard, frontend and backend impl. and fix windows pid lock for ws (#2339)
* feat(launcher): replace token-in-logs auth with standard HTTP login flow

## Problem

Previously users had to find the one-time token from console logs or
log files to access the dashboard - a non-standard, error-prone workflow
with no clear path for changing credentials.

## Solution: standard HTTP API login with bcrypt-backed password store

### Auth flow (new)
1. First run: browser opens, session guard detects uninitialized state,
   redirects to /launcher-setup
2. User sets a password (min 8 chars) via POST /api/auth/setup {password, confirm},
   bcrypt(cost=12) hash stored in ~/.picoclaw/launcher-auth.db (SQLite)
3. Subsequent logins: POST /api/auth/login {password}, HttpOnly cookie
   picoclaw_launcher_auth (HMAC-SHA256 signed, 7-day expiry)
4. 401 on any API call, frontend redirects to /launcher-login
5. Logout: POST /api/auth/logout, cookie cleared, redirect to login

### Backend changes
- web/backend/api/auth.go: renamed Token to Password; added handleSetup;
  launcherAuthStatusResponse now includes Initialized bool; PasswordStore
  interface wires bcrypt store into handlers
- web/backend/dashboardauth/: new package - Store with New(dir) / Open(path);
  SetPassword (bcrypt cost=12), VerifyPassword, IsInitialized
  - sql.go: all DB-layer constants (DBFilename, sqliteDriver, bcryptCost,
    four SQL query strings) - compile-time constants, zero runtime overhead
- web/backend/middleware/launcher_dashboard_auth.go: /launcher-setup and
  /api/auth/setup added to public paths
- web/backend/main.go:
  - dashboardauth.New(picoHome) replaces manual path construction
  - maskSecret(): suffix only revealed when >=5 chars hidden (length >= 12),
    preventing 8-char minimum passwords from leaking their tail
- web/backend/main_test.go: TestMaskSecret updated with boundary cases

### Forward-compatibility: pkg/credential integration

If the dashboard password is later reused as the enc:// passphrase,
the bcrypt hash in launcher-auth.db becomes an offline oracle.
Recommended mitigation (not yet implemented): derive two independent
subkeys via HKDF before use:

  bcrypt(HKDF(password, info="picoclaw-dashboard-login-v1"))  stored in DB
  HKDF(password, info="picoclaw-credential-enc-v1")           passed to PassphraseProvider

This isolates the two domains: cracking the bcrypt hash yields only the
login subkey, which is computationally independent of the enc:// subkey.

* fix(auth): replace wastedassign ok := false with var ok bool

* refactor(tray): remove copy-token clipboard feature

Dashboard login now uses standard web auth (bcrypt + session cookie).
The system tray 'Copy dashboard token' menu item is no longer needed.

- Delete tray_offers_copy.go and tray_offers_copy_stub.go
- Remove mCopyTok menu item and clipboard handler from systray.go
- Remove launcherDashboardTokenForClipboard var from main.go
- Remove MenuCopyToken/MenuCopyTokenHint keys from i18n.go

* feat(launcher-ui): standard HTTP login/setup/logout flow for dashboard

Replaces the previous "find token in logs" workflow with a proper
browser-based authentication UI backed by the new /api/auth/* endpoints.

### New pages
- /launcher-setup: first-run password initialization form (password +
  confirm, min 8 chars); calls POST /api/auth/setup; redirects to login
  on success
- /launcher-login: standard password login form; calls POST /api/auth/login;
  sets HttpOnly session cookie on success

### Session guard (src/routes/__root.tsx)
A useEffect on every non-auth page load calls GET /api/auth/status:
- initialized=false  -> redirect to /launcher-setup
- authenticated=false -> redirect to /launcher-login
This ensures the setup/login UI is shown even when the ?token= URL
mechanism auto-logs in (first-run case).

### Logout button (src/components/app-header.tsx)
IconLogout button added to the header with a confirm AlertDialog;
calls POST /api/auth/logout then redirects to /launcher-login.

### API layer
- src/api/launcher-auth.ts: LauncherAuthStatus gains initialized bool;
  postLauncherDashboardSetup() added; LauncherAuthTokenHelp removed
- src/api/http.ts: 401 guard uses isLauncherAuthPathname() (covers both
  /launcher-login and /launcher-setup) to prevent redirect loops
- src/lib/launcher-login-path.ts: isLauncherSetupPathname() and
  isLauncherAuthPathname() added

### Routing
- src/routeTree.gen.ts: /launcher-setup route registered throughout
- src/routes/launcher-login.tsx: tokenHelp UI removed; useEffect added
  to redirect to setup when initialized=false

### i18n
- en.json / zh.json: launcherSetup block added; launcherLogin keys
  updated to use passwordLabel/passwordPlaceholder

* fix(lint): ts lint fixed 1

* fix(auth): detail auth error handle

* fix(login):  frontend web auth error handle

* fix(frontend): auth error handler 5xx
2026-04-08 21:43:51 +08:00
美電球 3e3b6aed90 fix(tools): message tool no longer suppresses reply to originating chat (#2180)
When the message tool sent to a different chat (e.g., a group), the
agent's final response to the originating chat was incorrectly skipped
because HasSentInRound() was a simple bool that didn't distinguish
targets. Replace with HasSentTo(channel, chatID) that tracks all
send targets per round and only suppresses when the target matches.

Fixes cross-conversation message causing "Processing..." to hang.
2026-04-08 21:40:12 +08:00
k 087e355885 test(agent): remove unused respondWithMediaHook field 2026-04-08 19:53:11 +09:00
lxowalle 51eecde01e Feat/support isolation (#2423)
* * completed

* * optimzie

* * fix format

* * fix pr check

* try to fix ci

* * Indicates that Windows does not support expos_paths, adding more mount paths for the Linux platform.

* fix isolation startup lifecycle and MCP transport wrapping

* fix isolation startup cleanup and optional Linux mounts

* fix isolation path handling for relative hooks

Preserve relative command and working-directory semantics when Linux isolation wraps subprocesses, and restore absolute argv path exposure to avoid startup regressions. Add hook coverage and docs updates so isolation-enabled process hooks keep working as configured.

* * fix ci
v0.2.6
2026-04-08 18:15:42 +08:00
ywj 8b3e502690 fix(feishu): enrich reply context for card and file replies (#2144)
* fix(feishu): enrich reply context for card and file replies

* refactor(feishu): extract reply functions to feishu_reply.go

- Move reply-related functions to new feishu_reply.go
- Move corresponding tests to feishu_reply_test.go
- Extract magic number 600 to maxReplyContextLen constant
- Unify replyTargetID/replyTargetFromMessage (prefer parent_id, fallback root_id)
- Add source comment for containsFeishuUpgradePlaceholder

* fix(feishu): skip API fallback for non-thread messages, prepend replied media refs

- resolveReplyTargetMessageID: only call fetchMessageByID fallback when
  ThreadId is set, avoiding unnecessary API calls for non-reply messages
- prependReplyContext: prepend replied media refs before current media refs
  to maintain correct ordering

* fix(feishu): add message cache for fetchMessageByID to avoid repeated downloads

- Add messageCache (sync.Map) to FeishuChannel struct
- Cache fetched messages with 30s TTL to avoid re-downloading attachments
  when multiple users reply to the same parent message in a thread
- Cleanup expired entries on read access (no background goroutine needed)

* fix(feishu): early-return for non-reply messages, add cache and fetchMessageByID comment

* fix: remove duplicate test and fix gci import order

* fix(feishu): remove duplicate prependReplyContext call
2026-04-08 14:26:17 +08:00
wenjie 7d16764674 fix(gateway): validate PID ownership and clean stale pid files (#2422)
* fix(gateway): validate PID ownership and clean stale pid files

- include `pid` in health responses for runtime PID verification
- add `RemovePidFileIfPID` to safely delete PID files only on PID match
- sanitize gateway PID data via process-command checks with health fallback
- ignore and remove stale/non-gateway PID files before gateway operations
- refuse stop/restart actions when the attached process is not a gateway
- update gateway and websocket tests to cover PID validation and safety paths

* test(seahorse): use shared in-memory SQLite DB in tests to fix async compaction failures

* test: remove unused sendMediaErr field from hook test mock
2026-04-08 14:23:21 +08:00
k 862421b146 docs: add Korean README translation 2026-04-08 13:42:57 +09:00
Harmoon ee29aaa871 Enhance hooks with respond action and comprehensive documentation (#2215)
* feat(hooks): add respond action for tool execution bypass

Add a new HookActionRespond that allows hooks to return tool results directly, skipping actual tool execution. This enables plugin tool injection, caching, and mocking capabilities.

- Add HookActionRespond constant and support in HookManager
- Extend ToolCallHookRequest with HookResult field
- Implement respond action handling in process hooks and agent loop
- Add comprehensive tests for respond and deny_tool actions
- Update documentation with hook actions table and examples

* docs(hooks): add JSON-RPC protocol and plugin tool injection documentation

Add comprehensive documentation for hook JSON-RPC protocol and plugin tool injection capabilities:

- Add "Hook Actions" section to README.zh.md explaining respond action for tool execution bypass
- Create hook-json-protocol.md/.zh.md detailing JSON-RPC 2.0 protocol for all hook methods
- Create plugin-tool-injection.md/.zh.md with complete examples for external tool implementation
- Document how hooks can inject tool definitions and return results via respond action
- Include Python and Go examples for weather query plugin implementation

* feat(agent): emit tool events and feedback for hook results

Add ToolExecStart event emission and tool feedback for hook results to ensure consistent behavior between normal tool execution and hook bypass scenarios. This maintains parity in event tracking and user feedback when tools are executed via hooks.

* style(agent): format whitespace in hook structs and constants

Remove trailing whitespace and standardize spacing in JSON struct tags, constants, and test data for improved code consistency.

* feat(hooks): add media support for plugin tool injection

Extend the hook respond action to support media file handling:
- Add `media` field for returning images and files from hooks
- Add `response_handled` field to control turn completion behavior
- When response_handled=true, media is automatically delivered to user
- When response_handled=false, media is passed to LLM for vision requests

This enables plugins to directly return generated images, downloaded
files, and other media content either to users or for LLM analysis.

* docs(hooks): document security implications of respond action

Add security boundary documentation explaining that the respond action
bypasses ApproveTool checks, allowing hooks to return results for any
tool without approval. Include recommendations for secure hook
implementation and code comments marking the security considerations.

Changes:
- Add "Security Boundaries" section to plugin-tool-injection docs
- Document bypass of approval checks and associated risks
- Provide security recommendations and example code
- Add inline security comments in hooks.go and loop.go

* refactor(agent): improve completeness of tool result cloning and hook processing

Extend cloneToolResult to properly copy ArtifactTags and Messages fields,
ensuring deep copies of all ToolResult data. Consolidate event emission
and user message handling to match the normal tool execution flow.

* fix(agent): align hook respond path with normal tool execution flow

The hook respond code path was missing several critical behaviors that
existed in normal tool execution:

- Add logging for tool calls with arguments preview
- Add is_tool_call metadata to user-facing messages
- Handle attachment delivery failures by setting error state and
  notifying LLM
- Set ResponseHandled=false when using bus for media delivery
- Check for steering messages and graceful interrupts after tool
  execution, skipping remaining tools when appropriate
- Poll for SubTurn results that arrived during tool execution

This ensures consistent behavior between hook-responded tool calls and
normally executed tool calls.

* test(agent): add tests for hook respond media error handling

Add comprehensive tests for the hook respond code path when media
delivery fails. Tests cover error media channel scenarios and verify
proper error state handling.

Also document that AfterTool is not called when using respond action,
as it provides the final answer directly (design decision).
2026-04-08 11:47:02 +08:00
wenjie 330de0c382 fix(agent): disable seahorse context manager on freebsd/arm (#2417)
* fix(agent): disable seahorse context manager on freebsd/arm

Exclude freebsd/arm from the seahorse-enabled build and route it to the
unsupported stub implementation.

This avoids freebsd/arm build failures caused by modernc sqlite/libc while
keeping picoclaw buildable on that target.

* build: bump Go version from 1.25.8 to 1.25.9

* ci: install and run govulncheck directly in PR workflow
2026-04-08 10:57:22 +08:00
corevibe555 6ce0306c66 fix: use per candidate provider for model_fallbacks (#2143)
* fix: use per-candidate provider for model_fallbacks

Each fallback model now uses its own api_base and api_key from
model_list instead of inheriting the primary model's provider config.

Previously, a single LLMProvider was created from the primary model's
ModelConfig and reused for all fallback candidates — only the model ID
string was swapped. This caused all fallback requests to be routed to
the primary provider's endpoint, making cross-provider fallback chains
non-functional (e.g., OpenRouter primary with Gemini fallback would
send the Gemini request to OpenRouter's API).

Fix: pre-create a per-candidate LLMProvider at agent initialization
time by looking up each candidate's ModelConfig from model_list. The
fallback run closure now selects the correct provider per candidate
via CandidateProviders map, falling back to agent.Provider when no
override is found.

Fixes #2140

Made-with: Cursor

test: add test for instance.go

fix: fix test

refactor: optimize

fix: fix Golang lint issues

chore: comment cleanup

* refactor: use resolvedModelConfig() instead of buildModelIndex()

* fix
2026-04-07 20:07:56 +08:00
Andy Lo-A-Foe 1fc2710999 feat(channels): add teams_webhook output-only channel (#2244)
Add Microsoft Teams webhook integration via Power Automate workflows.

Features:
- Output-only channel for sending notifications to Teams
- Multiple webhook targets with named configuration
- Required "default" target with automatic fallback
- Rich Adaptive Card formatting with full-width rendering
- Markdown table conversion to native Adaptive Card Tables
- Column widths based on header content length
- HTTPS-only webhook URL validation
- Proper error classification for retry behavior

Configuration:
- channels.teams_webhook.enabled: bool
- channels.teams_webhook.webhooks: map of named targets
  - Each target has webhook_url (SecureString) and optional title

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-07 19:24:27 +08:00
Guoguo 6a8552a664 fix(web): derive WebSocket URL from browser location instead of backend (#2405)
The frontend previously used ws_url returned by /api/pico/token, which
is built from the launcher's own port. Behind a reverse proxy this can
produce incorrect URLs (e.g. ws://localhost:18800 instead of the
proxy's public address).

Since the launcher already proxies /pico/ws on the same port, the
frontend can simply use window.location.host to construct the
WebSocket URL, which is always correct regardless of proxy layers.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:37:01 +08:00
wenjie 7bf6cbe1fa fix(gateway): harden PID liveness handling and websocket proxy state (#2403)
- treat `EPERM` from `signal(0)` as “process exists” on Unix
- classify malformed PID files as invalid and auto-remove them during read
- keep cached `pidData` only for transient races and downgrade `running` to `stopped` when the tracked process is gone
- refresh PID data on WebSocket proxy requests and reject stale cached gateway state
- add regression tests for invalid PID files, status downgrade, on-demand PID loading, and stale proxy rejection
2026-04-07 16:34:42 +08:00
LC 38a498e202 feat(provider): support custom headers injection for HTTP providers (#2402)
* feat(provider): support custom headers injection for HTTP providers

* fix(provider): resolve lint problem

* fix(provider): align stream user-agent and header precedence docs
2026-04-07 16:05:21 +08:00
eturn 778f939302 fix [BUG] WebUI cannot connect to the gateway started by WebUI (#2267)
#2213
2026-04-07 15:46:45 +08:00
BeaconCat 84edc462d6 assets: update WeChat QR code image (#2385)
Co-authored-by: BeaconCat <BeaconCat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 14:09:11 +08:00
Liu Yuan f0e6b7aa37 fix(seahorse): correct bm25 rank semantics in comments (#2360)
SQLite FTS5 bm25() returns negative values where numerically smaller
(more negative) indicates a better match. The official docs state:

  "The better the match, the numerically smaller the value returned."

Two comments incorrectly stated "closer to 0 = better match" and
"lower = better match". Updated all rank descriptions to use the
unambiguous "more negative = higher relevance" phrasing.

This matters because these comments are used as tool prompt hints
for LLM agents, and incorrect semantics could lead to wrong ranking
decisions.
2026-04-07 12:32:28 +08:00
wenjie 661ce5e311 fix(build): gate seahorse context manager on unsupported platforms (#2384)
- add build tags to exclude context_seahorse.go on mipsle and netbsd
- add context_seahorse_unsupported.go to keep registration and return a clear runtime error
- remove unused indirect dependency github.com/reiver/go-porterstemmer from go.mod and go.sum
2026-04-07 11:49:35 +08:00
dependabot[bot] c3e7396a3d build(deps): bump github.com/pion/rtp from 1.8.7 to 1.10.1 (#2290)
Bumps [github.com/pion/rtp](https://github.com/pion/rtp) from 1.8.7 to 1.10.1.
- [Release notes](https://github.com/pion/rtp/releases)
- [Commits](https://github.com/pion/rtp/compare/v1.8.7...v1.10.1)

---
updated-dependencies:
- dependency-name: github.com/pion/rtp
  dependency-version: 1.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 09:58:20 +08:00
dependabot[bot] 29277d4b3b build(deps): bump modernc.org/sqlite from 1.47.0 to 1.48.0 (#2289)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.47.0 to 1.48.0.
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-version: 1.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 09:56:46 +08:00
Guoguo 9ec27835cf fix(docker): add -console flag and open network for launcher (#2314)
- Add -console to Dockerfile CMD so launcher outputs logs to stdout,
  making docker logs work as expected
- Remove 127.0.0.1 bind from ports to allow public network access
- Add commented PICOCLAW_LAUNCHER_TOKEN env var example

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:34:54 +08:00
Liu Yuan 1175f4a62b feat(membench): add LOCOMO memory benchmark tool (#2353)
Benchmark tool comparing legacy session manager vs seahorse short memory
retrieval on the LOCOMO long-term conversational memory dataset.

- cmd/membench/: CLI with ingest/eval/report/run subcommands
- Mode A (legacy): recency-biased budget truncation baseline
- Mode B (seahorse): per-keyword trigram FTS5 search + expand
- Metrics: Token-Overlap F1 and Recall Hit Rate
- `make mem` builds, downloads data, runs benchmark end-to-end
2026-04-06 17:26:43 +08:00
Liu Yuan 15a70ac45c feat(seahorse): implement short-term memory engine (LCM) (#2285)
* feat(seahorse): implement short-term memory engine of seahorse

Add pkg/seahorse/ module implementing a SQLite-backed DAG-based summary
hierarchy for context management, ported from lossless-claw's LCM design:

- types.go + short_constants.go: core types (Message, Summary, Conversation,
  ContextItem) and configuration constants (fanout, token targets, thresholds)
- migration.go: idempotent DB schema with FTS5 trigram tokenizer for CJK
- store.go: full SQLite CRUD (conversations, messages, summaries DAG,
  context_items with ordinal gap numbering, FTS5 search)
- short_engine.go: Engine lifecycle (NewEngine, Ingest, Assemble, Compact),
  session pattern filtering (ignore/stateless glob→regex compilation),
  per-session mutex via sync.Map
- short_assembler.go: budget-aware context assembly with fresh tail protection
  (32 messages), oldest-first eviction, summary XML formatting, RebuildContextItems
- short_compaction.go: leaf compaction (messages→summary) and condensed
  compaction (summaries→higher-level summary), 3-level LLM escalation,
  CompactUntilUnder for emergency overflow
- short_retrieval.go: lookupByID, FTS5/LIKE search, recursive expand with
  token cap
- context_seahorse.go: agent.ContextManager adapter, registered as "seahorse",
  provider↔seahorse message type conversion (ToolCalls, tool_result)

* fix(seahorse): correct 3 adapter bugs in context management

- TokenCount: use full message (Content+ToolCalls+Media) instead of Content-only
- Empty Content: rebuild Content from tool_result Parts when stored empty
- Duplicate summaries: summaries only in Summary field, not in History messages
- Grep: fix SearchResult.Snippet→Content for summaries
- Schema: fix FTS5 SQL uses VIRTUAL TABLE not TEMP TABLE
- TestFTS5SQLConstants: verify FTS5 SQL syntax correctness
- Test: fix flaky TestCompactLeaf

* fix(agent): ingest steering messages into seahorse SQLite

Steering messages were only persisted to session JSONL but not ingested
into seahorse SQLite, causing them to be missing from context assembly.

Added `ts.ingestMessage(turnCtx, al, pm)` call in the steering message
injection block alongside the existing JSONL persistence.

Test: TestSeahorseSteeringMessageIngested verifies steering messages
appear in seahorse SQLite DB after being processed.

* fix(seahorse): address 3 blocking bugs from code review

- Fix resequenceContextItemsTx scan error handling (store.go:850)
  Changed `return err` to `return scanErr` to properly propagate scan errors
  instead of returning nil (which silently corrupts data)

- Fix sql.NullString for INTEGER column (store.go:847)
  Changed `mid` from sql.NullString to sql.NullInt64 since message_id
  is INTEGER in schema. Removed unnecessary strconv.ParseInt call.

- Fix compactCondensed fallback deleting non-candidate items
  Added ReplaceContextItemsWithSummary method for per-item deletion
  when candidates are not contiguous in ordinal space.
  Optimized to use range deletion when candidates are consecutive.

* fix(seahorse): pass Budget to Compact for correct condensed threshold

Issue #4 from PR review: When Budget was not passed to seahorse.Compact,
it defaulted to `tokensBefore * 0.75`, making `tokensBefore > budget`
always true and causing condensed compaction to trigger unnecessarily.

Changes:
- context_seahorse.go: Forward Budget from CompactRequest to CompactInput
- loop.go: Pass Budget (ContextWindow) in all 3 Compact calls
- Add test verifying condensed is skipped when tokens < threshold
- Fix lint issues in store.go and store_test.go

* fix(seahorse): add mutex for assembler lazy initialization

Issue #5 from PR review: The check-then-create pattern for e.assembler
was a data race when multiple goroutines called Assemble() concurrently:
    if e.assembler == nil {
        e.assembler = &Assembler{...}
    }

Changes:
- Add assemblerMu sync.Mutex to Engine struct
- Add initAssemblerOnce() using double-checked locking (same pattern as initCompactionOnce)
- Add TestAssemblerLazyInitRace to verify thread-safety

* fix(seahorse): handle non-consecutive depths in selectShallowestCondensationCandidate

Issue #8 from PR review: the loop iterated depth 0, 1, 2... assuming
consecutive keys, but break when key was missing caused deeper depths
to never be checked.

Fix: collect all existing depth keys, sort, then iterate in order.

* fix(seahorse): wrap DeleteMessagesAfterID and appendContextItems in transactions

- DeleteMessagesAfterID: wrap all DELETE operations in a transaction for
  atomicity, remove redundant manual FTS delete (handled by trigger)
- appendContextItems: use transaction to fix read-then-write race condition
- Add GetMaxOrdinalTx and resolveItemTokenCountTx for transaction-scoped queries
- Remove unused resolveItemTokenCount function

Fixes PR review issues 6 and 7.

* fix(seahorse): derive readable content from Parts and cap CompactUntilUnder iterations

- Derive readable content from MessageParts in AddMessageWithParts so
  FTS5 indexing and summary formatting can access tool call information
- formatMessagesForSummary and truncateSummary now fall back to Parts
  when Content is empty, fixing blank summaries for Part-based messages
- Add MaxCompactIterations (20) to prevent CompactUntilUnder infinite
  loops; exceeded iterations are logged as warnings
2026-04-05 09:05:16 +08:00
LC 71337b6f52 fix(tool): clarify write_file nested-JSON escape semantics and add tests (#2320)
* fix(tool): clarify write_file nested-JSON escape semantics and add tests

* fix(tool): improve formatting of escaping rules in CLI tool prompt

* fix(tool): align escape notation with function.arguments layer
2026-04-04 17:56:49 +08:00
Mauro 84e42d6904 Merge pull request #2316 from zeroznet/fix/help-banner-double-v
fix: avoid duplicate v in CLI help banner
2026-04-03 23:14:07 +02:00
Robert Bopko e8d92e4a36 test: update root help banner expectation 2026-04-03 21:59:57 +02:00
Robert Bopko cbd0798a56 fix: avoid duplicate v in CLI help banner 2026-04-03 19:58:52 +02:00
Mauro d8c5183d9a feat(mcp): store oversized text results as artifacts (#2308)
* feat(mcp): store oversized text results as artifacts

* feat(mcp): fix doc

* fix(mcp): preserve raw MCP payload in text artifacts

* fix(mcp): avoid leaking large text when artifact persistence fails

* chore(mcp): clarify inline text limit and cover artifact edge cases
2026-04-04 01:30:36 +08:00
wenjie bd56e10bb8 fix(web): improve logs panel scroll handling (#2305)
- forward refs through ScrollArea so logs can access the viewport
- keep logs pinned to the bottom only when the user is already near it
- apply import and className ordering cleanup across frontend components
v0.2.5
2026-04-03 15:37:23 +08:00
wenjie 7f7b4c430b feat(web): persist dashboard token in launcher config (#2304)
- add `launcher_token` to launcher config API/schema and save/load flow
- update dashboard token resolution order: env var -> launcher config -> random
- expose token source in startup logs and auth help metadata (including config path)
- add launcher token input to the config page and wire frontend form/API updates
- update login help/i18n copy and extend backend tests for new token-source behavior
2026-04-03 14:54:27 +08:00
wenjie f2a19ab947 feat(web): support image messages in pico chat (#2299) 2026-04-03 14:15:20 +08:00
Cytown f3ad5d9305 bug: fix typo in Makefile cause ln not work (#2301) 2026-04-03 12:43:39 +08:00
Cytown 5b116b093f fix comment in Makefile (#2300) 2026-04-03 11:49:24 +08:00
Cytown 170ae09606 fix windows make build error and support custom build env (#2281) 2026-04-03 11:39:37 +08:00
linhaolin1 b5ce6209fd feat: add VK channel support (#2276)
* feat: add VK channel support

- Add VK channel implementation using vksdk
- Support text messages and media attachments
- Implement Long Poll API for real-time messaging
- Add group chat support with trigger prefixes
- Add user whitelist (allow_from) configuration
- Add VK channel documentation

Files:
- pkg/channels/vk/: VK channel implementation
- pkg/config/config.go: Add VKConfig structure
- pkg/channels/manager.go: Register VK channel
- pkg/gateway/gateway.go: Import VK channel package
- docs/channels/vk/: Usage documentation

* test: add unit tests for VK channel

- Test channel initialization with various configurations
- Test allow_from whitelist functionality
- Test group trigger configuration
- Test max message length (4000 chars)
- Test message splitting logic
- Test attachment processing

All tests passing ✓

* fix: resolve linting issues in VK channel

- Format VKConfig struct tags to comply with golines
- Remove unused mu sync.Mutex field
- Remove unused stripPrefix method

All tests passing ✓

* style: format VKConfig with golines

- Align struct tags to match project style
- Match formatting with other channel configs (Telegram, etc.)
- Fix golines linting error

* style: fix struct tag formatting in config.go

* docs: update VK channel docs to use secure token storage

* feat(vk): add voice capabilities support

- Implement VoiceCapabilities() method for VK channel
- Add audio_message attachment handling in processAttachments
- Add comprehensive tests for voice capabilities
- Support both ASR (speech-to-text) and TTS (text-to-speech)

* docs: add VK channel to documentation and update voice support

- Add VK channel to README.md and README.zh.md channel lists
- Update VK channel documentation with voice message support
- Document ASR and TTS capabilities for VK channel
- Add voice transcription configuration reference
2026-04-03 10:56:26 +08:00
lxowalle 849e37cf79 * Load zoneinfo from TZ and ZONEINFO env (#2279) 2026-04-03 10:12:15 +08:00
dependabot[bot] 8aa110c02a build(deps): bump shadcn from 4.1.1 to 4.1.2 in /web/frontend (#2297)
Bumps [shadcn](https://github.com/shadcn-ui/ui/tree/HEAD/packages/shadcn) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/shadcn-ui/ui/releases)
- [Changelog](https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/CHANGELOG.md)
- [Commits](https://github.com/shadcn-ui/ui/commits/shadcn@4.1.2/packages/shadcn)

---
updated-dependencies:
- dependency-name: shadcn
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 09:21:52 +08:00
dependabot[bot] 7fd6772196 build(deps): bump @tanstack/react-query in /web/frontend (#2296)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.95.2 to 5.96.1.
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.96.1/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.96.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 09:21:27 +08:00
dependabot[bot] 4169eb3b72 build(deps): bump react-i18next from 16.6.6 to 17.0.2 in /web/frontend (#2295)
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 16.6.6 to 17.0.2.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v16.6.6...v17.0.2)

---
updated-dependencies:
- dependency-name: react-i18next
  dependency-version: 17.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 09:20:58 +08:00
dependabot[bot] 8dfea249da build(deps-dev): bump eslint-plugin-react-refresh in /web/frontend (#2294)
Bumps [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) from 0.4.26 to 0.5.2.
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.4.26...v0.5.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-react-refresh
  dependency-version: 0.5.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 09:11:09 +08:00
dependabot[bot] 465baba994 build(deps): bump i18next from 26.0.1 to 26.0.3 in /web/frontend (#2292)
Bumps [i18next](https://github.com/i18next/i18next) from 26.0.1 to 26.0.3.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.0.1...v26.0.3)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 09:10:38 +08:00
Mauro f542c92970 Merge pull request #2288 from sipeed/dependabot/go_modules/github.com/rs/zerolog-1.35.0
build(deps): bump github.com/rs/zerolog from 1.34.0 to 1.35.0
2026-04-02 22:35:21 +02:00