Commit graph

2183 commits

Author SHA1 Message Date
lc6464
ee634dc8db
refactor(providers): reorganize provider packages and facades 2026-04-17 12:42:03 +08:00
sky5454
b0d3f19a6a docs(agent-refactor): document loop.go file split
Add loop-split.md explaining the 12-file split of the original
4384-line loop.go, covering the file map, extraction method,
and future phase 2 plans.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 11:22:37 +08:00
sky5454
12d5421c26 refactor(agent): split loop.go into focused sub-packages
Break up the monolithic 4384-line loop.go into 12 focused files:
- loop.go: core AgentLoop struct and main Run loop
- loop_turn.go: turn execution logic (runTurn, askSideQuestion, etc.)
- loop_utils.go: pure utility functions (formatters, helpers)
- loop_init.go: constructor and tool registration
- loop_message.go: message handling (processMessage, routing)
- loop_command.go: command processing (/use, /btw, etc.)
- loop_mcp.go: MCP runtime management
- loop_event.go: event/hook system helpers
- loop_media.go: media resolution and artifact handling
- loop_outbound.go: response publishing
- loop_transcribe.go: audio transcription
- loop_steering.go: steering queue and continuation
- loop_inject.go: setter injection methods

No functional changes - pure code movement with updated imports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 11:17:12 +08:00
dependabot[bot]
72f30c58e9
build(deps-dev): bump @types/node from 25.5.0 to 25.6.0 in /web/frontend (#2562)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.5.0 to 25.6.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.6.0
  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-17 10:30:11 +08:00
dependabot[bot]
235cb11beb
build(deps-dev): bump globals from 17.4.0 to 17.5.0 in /web/frontend (#2561)
Bumps [globals](https://github.com/sindresorhus/globals) from 17.4.0 to 17.5.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.4.0...v17.5.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.5.0
  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-17 10:24:18 +08:00
dependabot[bot]
74856d3747
build(deps): bump @tanstack/react-query in /web/frontend (#2560)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.97.0 to 5.99.0.
- [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.99.0/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.99.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-17 10:23:07 +08:00
dependabot[bot]
c36a48cf4b
build(deps): bump react-i18next from 17.0.2 to 17.0.3 in /web/frontend (#2559)
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 17.0.2 to 17.0.3.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v17.0.2...v17.0.3)

---
updated-dependencies:
- dependency-name: react-i18next
  dependency-version: 17.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-17 10:20:32 +08:00
dependabot[bot]
e77c4eba3e
build(deps): bump maunium.net/go/mautrix from 0.26.4 to 0.27.0 (#2557)
Bumps [maunium.net/go/mautrix](https://github.com/mautrix/go) from 0.26.4 to 0.27.0.
- [Release notes](https://github.com/mautrix/go/releases)
- [Changelog](https://github.com/mautrix/go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mautrix/go/compare/v0.26.4...v0.27.0)

---
updated-dependencies:
- dependency-name: maunium.net/go/mautrix
  dependency-version: 0.27.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-17 10:14:08 +08:00
dependabot[bot]
d73897da8e
build(deps): bump @tanstack/react-router in /web/frontend (#2555)
Bumps [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) from 1.168.8 to 1.168.22.
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.168.22/packages/react-router)

---
updated-dependencies:
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.168.22
  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-17 10:11:47 +08:00
dependabot[bot]
9c97442f7c
build(deps): bump go.mau.fi/util from 0.9.7 to 0.9.8 (#2553)
Bumps [go.mau.fi/util](https://github.com/mautrix/go-util) from 0.9.7 to 0.9.8.
- [Release notes](https://github.com/mautrix/go-util/releases)
- [Changelog](https://github.com/mautrix/go-util/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mautrix/go-util/compare/v0.9.7...v0.9.8)

---
updated-dependencies:
- dependency-name: go.mau.fi/util
  dependency-version: 0.9.8
  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-17 10:09:16 +08:00
dependabot[bot]
6375440152
build(deps): bump pnpm/action-setup from 4 to 6 (#2552)
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 6.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v6)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '6'
  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-17 10:07:10 +08:00
daming大铭
928a27359f
Merge pull request #2549 from lc6464/gateway-auth-no-browser
feat(auth): add no-browser option for OAuth login
2026-04-17 09:54:03 +08:00
daming大铭
ba08d52351
Merge pull request #2474 from srcrs/fix-cron-independent-sessions
fix(cron): make each job execution use an independent session
2026-04-16 23:52:29 +08:00
daming大铭
b1475122da
Merge pull request #2547 from lc6464/chore/issue-2538-network-fallback
feat(network): improve network error classification and fallback handling
2026-04-16 23:17:20 +08:00
lc6464
ffd30d7db7
fix(auth): improve no-browser OAuth login 2026-04-16 23:01:28 +08:00
daming大铭
eb24269651
Merge pull request #2503 from cytown/loop
refactor: make agent loop support parallel and update docs
2026-04-16 22:47:34 +08:00
lc6464
2b844778ff
refactor(tests): extract common logic for fallback error handling into a helper function 2026-04-16 22:45:31 +08:00
lc6464
ab019d3f18
feat(auth): add no-browser option for OAuth login 2026-04-16 22:19:34 +08:00
LC
7aa2d672ce
fix(network): classify timeout errors as FailoverTimeout
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-16 22:00:13 +08:00
lc6464
c3f4000817
feat(network): implement network error classification and fallback handling 2026-04-16 19:59:37 +08:00
wenjie
7fdc9c7b64
fix(web): support proxies in SearXNG and web fetch (#2542)
Propagate the configured HTTP client and proxy settings to the
SearXNG search provider.

Allow web_fetch to connect to the configured proxy as the first hop
without bypassing the existing private-host checks for redirect
targets and fetched URLs.

Add tests for loopback proxy fetches and SearXNG proxy propagation.
2026-04-16 17:15:47 +08:00
wenjie
7f56ca8cc6
feat(web): refactor tools page into tabbed library and web search settings (#2539)
- split the tools page into focused components and a shared hook
- add separate Tool Library and Web Search tabs
- refresh web search settings layout and localized copy
- make provider expansion keyboard accessible
- restore wrapping for long tool names in library cards
- allow custom styling for KeyInput
2026-04-16 17:14:35 +08:00
Cytown
f5e779e22e refactor: make agent loop support parallel and update docs 2026-04-16 14:43:15 +08:00
lxowalle
e22b4e1eee
feat(agent): support btw side questions (#2532) 2026-04-16 10:53:09 +08:00
wenjie
a8d0b03515
fix(web): save channel configs with nested channel_list patches (#2530)
Persist channel settings through the current channel_list schema, keeping common
channel fields at the top level and channel-specific fields under settings.
Return common fields and default config shapes from channel config endpoints, and
add coverage for nested patches, missing channel defaults, and secret handling.
2026-04-16 10:30:16 +08:00
wenjie
f32b303d2a
fix(web): avoid resetting web search draft on config refetch (#2536) 2026-04-16 10:26:18 +08:00
xiaoen
a34120b821 test(agent): assert child turn uses target agent model
Replace generic mockProvider with modelRecordingProvider that captures
the model parameter passed to Chat(). After delegation from alpha to
beta, assert the recorded model is "model-beta" — proving the child
turn actually ran with the target agent's configuration, not the
caller's.

Also add wiring tests:
- TestDelegateToolNotRegistered_SingleAgent: single-agent has no
  delegate in its tool registry
- TestDelegateToolRegistered_MultiAgent: both agents in a two-agent
  setup have the delegate tool

Ref: #2148
2026-04-15 22:27:05 +08:00
xiaoen
6ee66123f2 refactor(agent): simplify delegate registration gate
Remove the IsToolEnabled("delegate") check — there is no "delegate"
entry in ToolsConfig, so the check was always true. The only real
gate is len(agents) > 1, which is the intended behavior: delegate
is auto-registered in multi-agent setups.

Ref: #2148
2026-04-15 22:24:47 +08:00
xiaoen
6db17b8211 test(tools): verify normalization prevents self-delegation bypass
Add table-driven test with case and whitespace variants (ALPHA,
" Alpha ", "  alpha  ") that should all be caught by the self-check
after normalization.

Ref: #2148
2026-04-15 22:23:47 +08:00
xiaoen
df486b9939 fix(tools): normalize agent_id before self-check and delegation
Apply routing.NormalizeAgentID to the raw agent_id input before any
logic runs. This prevents case/whitespace variants like "ALPHA" or
" alpha " from bypassing the self-delegation guard while still
resolving to the same agent in the registry.

The normalized value is used consistently for self-check, allowlist,
SpawnSubTurn, and result attribution.

Ref: #2148
2026-04-15 22:23:17 +08:00
ex-takashima
5b0c9e2708 Merge remote-tracking branch 'origin/main' into refactor/line-sdk
# Conflicts:
#	pkg/channels/line/line.go
2026-04-15 23:07:04 +09:00
xiaoen
039f35563e feat(agent): wire delegate tool registration for multi-agent setups
Register the delegate tool in registerSharedTools when multiple agents
are configured. Gated independently from the subagent tool — delegate
uses SubTurn directly and does not depend on SubagentManager.

Self-delegation is prevented by injecting the current agent ID.
Permission is enforced via CanSpawnSubagent (reuses allow_agents config).

Single-agent setups are unaffected: the tool is not registered when
only one agent exists in the registry.

Ref: #2148
2026-04-15 21:29:29 +08:00
xiaoen
0ff78fa53f test(tools): add delegate tool unit tests
12 test cases covering:
- success path with result attribution
- agent_id validation (missing, empty, whitespace, wrong type)
- task validation (missing, empty, whitespace)
- permission denied / allowed via allowlist checker
- self-delegation blocked
- nil spawner, spawner error, nil result from spawner
- open access when no allowlist checker is set

Ref: #2148
2026-04-15 21:28:54 +08:00
xiaoen
484ef399f1 feat(tools): add delegate tool for synchronous cross-agent task handoff
delegate(agent_id, task) hands off a task to a named agent and blocks
until the result is ready. The target agent runs with its own config
via the TargetAgentID mechanism in SubTurnConfig.

Key behaviors:
- Self-delegation explicitly rejected
- Permission gated by subagents.allow_agents (D4)
- Spawner errors preserve the underlying error via WithError
- Nil result from spawner handled gracefully
- Response attributed with target agent ID

Ref: #2148
2026-04-15 21:28:31 +08:00
xiaoen
c8335bfd47 test(agent): verify TargetAgentID resolves to correct agent instance
Add multi-agent test setup (newMultiAgentLoop) with two agents using
distinct models (model-alpha, model-beta).

Three new tests:
- UsesTargetAgent: parent=alpha delegates to beta, event log confirms
  child runs as agent_id=beta with model=model-beta
- NotFound: TargetAgentID pointing to nonexistent agent returns error
- EmptyModelAccepted: empty Model field accepted when TargetAgentID
  provides the model implicitly

Ref: #2148
2026-04-15 21:27:39 +08:00
xiaoen
c47f5fd2c4 feat(agent): add TargetAgentID to SubTurnConfig for cross-agent delegation
When TargetAgentID is set, spawnSubTurn resolves the target AgentInstance
from the registry and uses it as the base for the child turn. This gives
the child turn the target's workspace, model, tools, and system prompt
instead of inheriting from the caller.

Model validation is relaxed: empty Model is accepted when TargetAgentID
provides the model implicitly via the resolved agent instance.

Ref: #2148
2026-04-15 21:27:13 +08:00
BeaconCat
f1b659e5ef
membench: add LLM-as-Judge evaluation mode (#2484)
* membench: add LLM-as-Judge evaluation mode

Add --eval-mode=llm to membench for LLM-based answer generation and
semantic scoring via an OpenAI-compatible API endpoint.

New files:
- llm_client.go: generic OpenAI-compatible chat completion client
  with support for API key, configurable timeout, and optional
  chat_template_kwargs (for llama.cpp thinking models)
- eval_llm.go: LLM answer generation + LLM-as-Judge scoring for
  both legacy and seahorse retrieval modes

Changes to main.go:
- --eval-mode flag (token|llm) to select evaluation strategy
- --api-base, --api-key, --model flags with env var fallback
  (MEMBENCH_API_BASE, MEMBENCH_API_KEY, MEMBENCH_MODEL)
- --no-thinking flag for llama.cpp + Qwen thinking models
- --limit flag to cap QA questions per sample for quick testing

* style: fix golangci-lint formatting (gofmt + golines)

* fix: address Copilot review feedback

- Validate --model is required for LLM eval mode
- Use rune-based truncation to preserve valid UTF-8
- Precompute totalQA count outside inner loop
- Log SearchMessages errors instead of silently skipping

* fix: address Copilot review round 2

- Validate --eval-mode accepts only 'token' or 'llm'
- Normalize base URL to avoid /v1/v1 duplication
- Separate token/LLM results for correct PrintComparison labeling
- Log ExpandMessages errors instead of silently ignoring
- Short-circuit with 0 scores when no context retrieved (match token eval)
- Add --timeout flag wired to LLMClientOptions.Timeout

* fix: address review P1+P2 — sort alignment, failure sentinel, score parser

- P1: Replace hand-rolled sortByRank with sort.Slice (ascending, best
  first) matching eval.go's EvalSeahorse — ensures BudgetTruncate keeps
  best-ranked messages when truncation occurs
- P2: Use -1.0 sentinel for LLM API failures and parse errors, distinct
  from genuine 0.0 score; aggregateMetrics skips -1.0 entries for F1
  averaging while still counting HitRate
- P2: Use regexp \b([1-5])\b for judge score extraction instead of
  first-digit scan — avoids misparses on '5/5', 'Score: 3' etc.

* fix: address Copilot review round 2

- Fix F1/HitRate weighted aggregation: track ValidF1Count separately so
  computeModeAgg weights F1 by valid scores only, not TotalQuestions
- No-context retrieval failure uses 0.0 (genuine bad score) instead of
  -1.0 sentinel (reserved for API/parse failures)
- Validate --timeout > 0 to prevent disabling HTTP timeouts

* fix: remove hardcoded /v1 from API base URL

Users now provide the full versioned path in --api-base (e.g. /v1, /v4).
Code only appends /chat/completions. Default changed to
http://127.0.0.1:8080/v1 for backward compatibility.

* fix: address Copilot review round 3

- ValidF1Count=0 when all scores are sentinel (no forced =1)
- Backward compat: old eval JSON without ValidF1Count falls back to
  TotalQuestions in computeModeAgg
- Skip empty section in PrintComparison when tokenResults is empty
- Update --api-base flag help to document /v1 default and version path
- Add sentinel aggregation unit tests (partial, all, weighted)

* feat: add --retries flag with exponential backoff for transient LLM errors

Retry on timeout, 5xx, and 429 (rate limit) with 1s/2s/4s backoff.
Default 3 retries, configurable via --retries. Context cancellation
is respected between retries.

* fix: address Copilot review round 4

- runReport splits results by mode suffix into token/llm for PrintComparison
- backward compat fallback (ValidF1Count=0 -> TotalQuestions) only for
  non-LLM modes; LLM modes keep ValidF1Count=0 when all scores sentinel
- MaxRetries==0 means no retry; only negative falls back to default 3
- truncateStr uses []rune to avoid cutting multi-byte UTF-8 characters
- Complete() returns error on empty LLM response (vs silent empty string)

* feat: --no-thinking adapts to llama.cpp, Ollama, and GLM backends

Send all three disable-thinking fields simultaneously:
- chat_template_kwargs.enable_thinking=false (llama.cpp, GLM)
- think=false (Ollama 0.9+)
- thinking.type=disabled (GLM/Zhipu)
Each backend picks the field it recognizes and ignores the rest.
Also bumps max_tokens from 512 to 2048 for thinking models.

* feat: mixed model eval + concurrent QA workers

- Add --judge-model, --judge-api-base, --judge-api-key flags for separate judge model
- Add --concurrency flag (default 1) with semaphore-based goroutine pool
- Add reasoning_content fallback for GLM/DeepSeek style responses
- Prepend /no_think to system prompt for Ollama /v1 compatibility
- Reduce default MaxTokens from 2048 to 512 (answers are 1-3 sentences)
- Extract evalQAWorker and buildSeahorseContext for shared concurrent logic

---------

Co-authored-by: BeaconCat <BeaconCat@users.noreply.github.com>
2026-04-15 21:15:17 +08:00
美電球
ead2dc9699
Merge pull request #2524 from SiYue-ZO/feature/sogou-web-search-default
Add configurable Sogou-backed web search
2026-04-15 20:50:53 +08:00
wenjie
7bd11181a6
fix(agent): preserve reused tool call IDs across turns (#2528)
Scope tool result deduplication to each assistant tool-call block so providers
that reuse call IDs across separate turns do not lose valid tool results. Also
drop invalid empty tool call IDs and orphaned tool messages after validation.
2026-04-15 20:18:09 +08:00
daming大铭
100e576609
Merge pull request #2529 from lc6464/feat/web-code-highlight
feat(web): add markdown syntax highlighting for chat and skills
2026-04-15 18:50:40 +08:00
SiYue-ZO
2784223ad5 Make web search auto-switch with UI language
Default the sample web search provider to auto, route Sogou vs DuckDuckGo dynamically based on query/UI language, and sync frontend language changes back to the backend so Current Service and runtime selection stay aligned.
2026-04-15 18:45:28 +08:00
lc6464
5a2e7795cd
refactor(web): improve theme style element management in useHighlightTheme hook 2026-04-15 18:30:43 +08:00
lc6464
acbe654674
chore(web): move app providers out of main entry 2026-04-15 17:36:22 +08:00
lc6464
389f492d8c
refactor(web): use official highlight themes for markdown 2026-04-15 17:19:48 +08:00
lc6464
25ac563406
feat(web): add syntax highlighting for markdown code blocks 2026-04-15 14:54:13 +08:00
Mauro
bb14a5c7cc
Merge pull request #2525 from afjcjsbx/fix/vision-unsupported-media-stuck
fix(agent): recover after image-input-unsupported failures
2026-04-15 07:54:33 +02:00
SiYue-ZO
bb953b788b test(api): fix web tools lint issues 2026-04-15 13:35:39 +08:00
SiYue-ZO
75e93b5189 Merge remote-tracking branch 'upstream/main' into feature/sogou-web-search-default
# Conflicts:
#	pkg/tools/web.go
#	pkg/tools/web_test.go
2026-04-15 13:28:05 +08:00
SiYue-ZO
0b84f0ae0a fix(web): address sogou search review feedback 2026-04-15 13:03:06 +08:00
Cytown
d0ff24aa87
remove useless backend output for platform-token (#2500) 2026-04-15 11:38:47 +08:00