Commit Graph
3201 Commits
Author SHA1 Message Date
Xubin Ren d8601478db test: cover stream-id delta coalescing 2026-06-27 16:47:53 +08:00
axelray-devandXubin Ren 66fc54421c fix: include _stream_id in stream delta coalescing key (#4063)
ChannelManager coalesces _stream_delta messages by (channel, chat_id)
only. Overlapping streams in the same chat can be merged incorrectly
because deltas from distinct _stream_id values share one buffer.

Include _stream_id in the coalescing key so distinct streams in the
same channel and chat are delivered separately.
2026-06-27 16:47:53 +08:00
Xubin Ren 6a27c26257 test: cover non-stream duplicate tool call ids 2026-06-27 16:47:48 +08:00
axelray-devandXubin Ren 3ca82ea880 fix: deduplicate tool call IDs in non-stream parser (#4059)
Duplicate tool call ID normalization exists in the streaming parser path
but is not shared with the non-stream parser. Non-stream parsing appends
raw provider IDs into ToolCallRequest objects without deduplication.

Some OpenAI-compatible providers reuse the same tool_call_id for parallel
tool calls in non-streaming responses. Without dedup, runner executes
both tools with the same ID, producing duplicate tool results with the
same tool_call_id, which can fail strict provider validation.

Add the same _seen_tc_ids dedup pattern used in _parse_chunks to the
_parse method so both paths handle duplicate IDs consistently.
2026-06-27 16:47:48 +08:00
r4sk1nandXubin Ren 47dcc61e9b test(agent): fix flaky test_keeps_n_most_recent by ensuring sequential mtimes 2026-06-27 16:29:31 +08:00
axelray-devandXubin Ren 2bf111f456 fix(exec): remove ad-hoc shell comment stripping from _guard_command
- Removes match_text regex that stripped # comments before pattern matching
(broke on quoted # inside strings)
- allow_patterns now run re.fullmatch against the full lowercased command
- deny_patterns search the original lowercased command
- Replaces comment-stripping test with comment-tail bypass regression
(touch canary # echo allowlisted must be blocked)
- Adds Re-bin regression for quoted hash + blocked command
(echo "#" followed by blocked command must be caught)
- All 10 tests pass

Signed-off-by: axelray-dev <110029405+axelray-dev@users.noreply.github.com>
2026-06-27 11:11:46 +08:00
axelray-devandXubin Ren aa6c1bf300 fix(exec): prevent allowPatterns bypass via chained commands and shell comments 2026-06-27 11:11:46 +08:00
chengyongruandXubin Ren 5281e67222 fix(docker): repair whatsapp image build 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren dbb53109f4 build(docker): remove node from runtime image 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren b015515f30 docs(security): remove whatsapp bridge wording 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren be88e14424 docs(security): trim whatsapp migration note 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren 9e490ef473 docs(readme): restore changelog wording 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren bfb4246659 fix(docker): limit whatsapp bridge removal 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren fbf96a3502 fix(whatsapp): add bridge migration compatibility 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren 2a9e288dfe refactor(whatsapp): replace bridge with neonize 2026-06-27 11:05:03 +08:00
chengyongruandXubin Ren 3460ca3cb9 fix(agent): gate microcompaction on context pressure
Extract model-facing context governance from AgentRunner.

Only compact in-flight tool results when the model request is over budget, keep compacted IDs stable within a turn, and allow the newest result to be compacted as a last resort when it is the remaining source of overflow.
2026-06-27 11:04:41 +08:00
chengyongruandXubin Ren 9b45fc1172 fix(session): remove message time replay prefixes 2026-06-27 11:04:36 +08:00
chengyongruandXubin Ren 8656549129 test: cover exec login default public path
maintainer edit: add a regression test for the public ExecTool.execute path so omitted login stays non-login by default, and update the Unix environment docstring to match the new explicit login behavior.
2026-06-27 11:04:31 +08:00
axelray-devandXubin Ren 4c1f127549 test: update login-shell assertion for new default=False 2026-06-27 11:04:31 +08:00
axelray-devandXubin Ren 13c951aa41 fix: change exec login-shell default from true to false (#4518)
The exec tool defaults login=True for bash/zsh, which causes the shell
to source ~/.bash_profile and similar startup files. This reintroduces
secrets from shell startup files into the exec environment, even though
_build_env() intentionally starts with a curated environment.

Change the default to login=False in both _prepare_command() and _spawn(),
and update the schema default accordingly.
2026-06-27 11:04:31 +08:00
chengyongruandXubin Ren cd1fb61eb5 ci: relax webui install lock check 2026-06-27 11:04:11 +08:00
chengyongruandXubin Ren e79cb816e3 ci: pin bun for webui job 2026-06-27 11:04:11 +08:00
chengyongruandXubin Ren 64901be67f test: harden webui and gateway checks 2026-06-27 11:04:11 +08:00
chengyongruandXubin Ren 9ce9d2235a docs: clarify heartbeat versus cron delivery 2026-06-27 11:04:08 +08:00
Xubin Ren 06d5495b60 docs: document subagent tool error behavior 2026-06-25 22:53:36 +08:00
axelray-devandXubin Ren 851a0ff50c fix: make subagent fail_on_tool_error configurable (#4198)
Add fail_on_tool_error to AgentDefaults and wire it through
AgentLoop -> SubagentManager -> AgentRunSpec.

Previously hardcoded to True in SubagentManager._run_subagent.
Now configurable via config.json with default True for backward
compatibility. When set to False, subagents can retry on minor
tool errors instead of immediately failing.

Changes:
- nanobot/config/schema.py: add fail_on_tool_error field (default True)
- nanobot/agent/subagent.py: accept and forward fail_on_tool_error
- nanobot/agent/loop.py: pass config through to SubagentManager
- tests/agent/test_subagent.py: add regression test

Signed-off-by: axelray-dev <110029405+axelray-dev@users.noreply.github.com>
2026-06-25 22:53:36 +08:00
Xubin Ren 3596ccf828 docs: explain custom provider thinking style 2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren d1ae73a8a8 fix: add clear error message for invalid thinking_style values
Widen thinking_style from Literal to str | None and add a
@field_validator that produces a helpful error message listing
valid options when an invalid value is provided.

Addresses the review feedback on #4482.
2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren c661012754 fix: coalesce None thinking_style to empty string in provider creation
ProviderConfig.thinking_style defaults to None (Optional field), but
create_dynamic_spec expects a string. Coalesce None to "" at all call
sites (factory.py, settings_api.py) and fix the test assertion to
expect None from the config default.
2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren 0e19ea3062 fix: validate thinking_style against known values at config load time 2026-06-25 22:53:15 +08:00
axelray-devandXubin Ren ceae6d7b61 fix: allow custom provider to configure thinking style (#4429) 2026-06-25 22:53:15 +08:00
Xubin Ren 34f776b48b test(cli): lock disabled dream cursor advancement 2026-06-25 22:53:10 +08:00
axelray-devandXubin Ren f7b027a295 fix: only advance dream cursor when behind latest (#4242)
Address review: avoid overwriting cursor on every restart when Dream
is disabled. Now only advances if current cursor is behind the latest
position, so repeated restarts don't permanently skip entries.
2026-06-25 22:53:10 +08:00
axelray-devandXubin Ren 6c880a6691 fix: advance dream cursor when Dream is disabled to prevent prompt bloat (#4242)
When dream.enabled is false, the Dream cron job never runs, so the
dream cursor (.dream_cursor) stays at its initial value (0). This
causes read_recent_history_for_prompt() to treat every history entry
as unprocessed, injecting the full chat history into every system
prompt and growing without bound.

Fix: fast-forward the dream cursor to the latest history entry at
gateway startup when Dream is disabled.
2026-06-25 22:53:10 +08:00
Xubin Ren 4636c78100 test(webui): cover xiaomi mimo wav recording path 2026-06-25 22:52:55 +08:00
zpljd258andXubin Ren 28c8c89a42 fix(webui): convert WebM to WAV for Xiaomi MiMo ASR transcription
MiMo ASR (mimo-v2.5-asr) only accepts audio/wav, audio/mp3, and
audio/mpeg formats. Web browsers record in WebM/Opus by default,
causing the API to reject the payload with a transcription error.

This change adds a frontend WebM→WAV converter using the Web Audio API
(DecodeAudioData + PCM encoding) that activates only when the
configured transcription provider is 'xiaomi_mimo'. Other providers
are unaffected — they continue to receive the original browser format.

Tested and confirmed working on WebUI.
2026-06-25 22:52:55 +08:00
7899857201 refactor(cli): simplify onboard search-provider dispatch
- Extract _set_field_from_choices for the shared pick-and-set tail used by
  both the LLM and search provider handlers.
- Replace the single-entry _TYPED_FIELD_HANDLERS registry with a direct
  isinstance check in _resolve_field_handler (robust to renames, no
  class-name strings).
- Drop a redundant str() in the search default computation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:52:50 +08:00
9354b80a6e fix(cli): show search engines (incl. Keenable) in onboard wizard
The onboard wizard dispatched field handlers by bare field name, so
WebSearchConfig.provider was hijacked by the LLM-provider handler and
showed LLM providers instead of search engines. Keenable was also never
wired into the CLI wizard when it landed in the WebUI.

- Add a single source of truth for selectable search providers
  (SEARCH_PROVIDER_OPTIONS in web.py); WebUI settings now import it.
- Add a WebSearchConfig-aware search-provider picker to the wizard and
  resolve handlers by (model type, field name) so the LLM and search
  provider fields no longer collide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:52:50 +08:00
Max HsuandXubin Ren 638af123ba fix(dingtalk): preserve richText formatting and set HTTP client timeout
richText messages only kept rich_text_list items whose type was
"text", so bold/italic/inlineCode/pre segments were silently dropped;
a message made entirely of formatted segments produced empty content
and fell through to the "unsupported message type: richText" warning.
Now any item carrying text is kept (matching the SDK's own
get_text_list, which keys off the "text" field) and its type is mapped
to Markdown so the formatting survives into the agent's context.

Text and downloadCode within a rich-text item are handled
independently (the SDK treats them as separate via get_text_list /
get_image_list), so an item carrying both a caption and an attachment
no longer drops the file.

The shared httpx.AsyncClient was created without a timeout, so all
requests (including large file/image downloads) used httpx's 5s
default and hit ConnectTimeout/ReadTimeout on uploads. Set an explicit
httpx.Timeout (connect=10s, read/write=30s).

Adds regression tests for formatted-segment preservation, the
all-formatted no-drop case, the text+downloadCode item, and the client
timeout configuration.

Closes #4497
2026-06-25 22:52:45 +08:00
michaelxerandXubin Ren 42aa37cfc0 docs: update enabledTools docs and schema comment to reflect resource/prompt gating
The enabledTools gate now also controls MCP resource and prompt
registration (not just tools). Update the configuration docs and
schema field comment to document this behavioral change.

Refs: #4435, #4436
2026-06-25 16:10:37 +08:00
michaelxerandXubin Ren 03302c751f log info when resources/prompts skipped due to enabledTools gate
Address chengyongru review: bump skip message from silent to logger.info
so operators get a visible trace when resources/prompts are not registered.
2026-06-25 16:10:37 +08:00
michaelxerandXubin Ren 246ea8ef61 fix(tools): gate MCP resource and prompt registration behind enabledTools
The enabledTools allowlist was only enforced for MCP tools returned by
session.list_tools(). Resources and prompts from session.list_resources()
and session.list_prompts() were registered unconditionally, allowing a
deny-all or restrictive enabledTools config to leak resource and prompt
capabilities to the model.

Now resources and prompts are only registered when allow_all_tools is
true (default ["*"] wildcard). Any explicit tool restriction — including
enabledTools: [] (deny-all) or a list of specific tool names — also
blocks resource and prompt registration from that server.

Fixes #4435
2026-06-25 16:10:37 +08:00
chengyongruandXubin Ren f60b3c7920 docs: explain Telegram rich messages opt-in
maintainer edit: document that richMessages defaults to false, when to enable it, and why Telegram Web users should leave it disabled.
2026-06-25 16:10:33 +08:00
chengyongruandXubin Ren e92899607a fix: make Telegram rich messages opt in
maintainer edit: Telegram Web cannot render sendRichMessage payloads, so keep the rich path available only for explicit opt-in instead of enabling it by default.
2026-06-25 16:10:33 +08:00
axelray-devandXubin Ren c930aa3713 fix: add rich_messages config to disable sendRichMessage for Telegram Web (#4488) 2026-06-25 16:10:33 +08:00
chengyongruandXubin Ren 4378944459 test: speed up test suite 2026-06-25 16:10:28 +08:00
chengyongruandXubin Ren 123384975e fix(webui): restore code block copy fallback 2026-06-25 16:10:23 +08:00
chengyongruandXubin Ren 943191f0c0 fix(webui): keep multi-file apply_patch edits 2026-06-24 20:04:39 +08:00
Xubin Ren c915e98c15 test: cover archived heartbeat target selection 2026-06-24 15:45:49 +08:00
Heng Wei BinandXubin Ren de4009efbd fix: exclude archived keys in heartbeat & fallback missing session timestamps 2026-06-24 15:45:49 +08:00