1769 Commits

Author SHA1 Message Date
Xubin Ren
63acfc4f2f test: fix trailing-space mismatch and add regression tests for normal models
- Fix assertion in streaming dict fallback test (trailing space in data
  not reflected in expected value).
- Add two regression tests proving that models with reasoning_content
  (e.g. DeepSeek-R1) and standard models (no reasoning fields) are
  completely unaffected by the reasoning fallback.

Made-with: Cursor
2026-04-08 00:59:39 +08:00
moranfong
12ff8b22d6 fix(provider): extend StepFun reasoning fallback to all code paths
- Add reasoning_content fallback from reasoning in _parse dict branch
- Add content fallback from msg.reasoning in _parse SDK object branch
- Add reasoning_content fallback in _parse SDK object branch
- Add reasoning fallback in _parse_chunks dict branch
- Add reasoning fallback in _parse_chunks SDK object branch

This ensures StepFun Plan API works correctly in both streaming and
non-streaming modes, for both dict and SDK object response formats.
2026-04-08 00:59:39 +08:00
moranfong
9e7c07ac89 test(provider): add StepFun reasoning field fallback tests
Add comprehensive tests for the StepFun Plan API compatibility fix:
- _parse dict branch: content and reasoning_content fallback to reasoning
- _parse SDK object branch: same fallback for pydantic response objects
- _parse_chunks dict branch: reasoning field handled in streaming mode
- _parse_chunks SDK branch: reasoning fallback for SDK delta objects
- Precedence tests: reasoning_content field takes priority over reasoning

Refs: fix(provider): support StepFun Plan API reasoning field fallback
2026-04-08 00:59:39 +08:00
moranfong
53107c6683 fix(provider): support StepFun Plan API reasoning field fallback
StepFun Plan API returns response content in the 'reasoning' field when
the model is in thinking mode and 'content' is empty. OpenAICompatProvider
previously only checked 'content' and 'reasoning_content', missing this field.

This patch adds a fallback: if content is empty and 'reasoning' is present,
extract text from reasoning to populate content, ensuring StepFun models
(step-3.5-flash, step-3.5-flash-2603) work correctly with tool calls.

Co-authored-by: moranfong <moranfong@gmail.com>
2026-04-08 00:59:39 +08:00
Xubin Ren
c736cecc28 chore(gitignore): remove bogus extensions and relocate nano.*.save
- Drop *.pycs, *.pywz, *.pyzz — not real Python file extensions.
- Move nano.*.save from "Project-specific" to "Editors & IDEs" where
  it belongs (nano editor backup files, not project artifacts).

Made-with: Cursor
2026-04-08 00:42:25 +08:00
Jack Lu
873bf5e692 chore: update .gitignore to include additional project-specific, build, test, and environment files 2026-04-08 00:42:25 +08:00
Xubin Ren
8871a57b4c fix(mcp): forward prompt arg descriptions & standardise error format
- Propagate `description` from MCP prompt arguments into the JSON
  Schema so LLMs can better understand prompt parameters.
- Align generic-exception error message with tool/resource wrappers
  (drop redundant `{exc}` detail).
- Extend test fixture to mock `mcp.shared.exceptions.McpError`.
- Add tests for argument description forwarding and McpError handling.

Made-with: Cursor
2026-04-08 00:28:04 +08:00
Tim O'Brien
7cc527cf65 feat(mcp): expose MCP resources and prompts as read-only tools
Add MCPResourceWrapper and MCPPromptWrapper classes that expose MCP
server resources and prompts as nanobot tools. Resources are read-only
tools that fetch content by URI, and prompts are read-only tools that
return filled prompt templates with optional arguments.

- MCPResourceWrapper: reads resource content (text and binary) via URI
- MCPPromptWrapper: gets prompt templates with typed arguments
- Both handle timeouts, cancellation, and MCP SDK 1.x error types
- Resources and prompts are registered during server connection
- Gracefully handles servers that don't support resources/prompts
2026-04-08 00:28:04 +08:00
Xubin Ren
ce7986e492 fix(memory): add timestamp and cap to recent history injection 2026-04-08 00:03:11 +08:00
Xubin Ren
05d8062c70 test: add regression tests for unprocessed history injection in system prompt
Made-with: Cursor
2026-04-07 23:41:05 +08:00
Lingao Meng
31c154a7b8 fix(memory): prevent potential loss of compressed session history
When the Consolidator compresses old session messages into history.jsonl,
those messages are immediately removed from the LLM's context. Dream
processes history.jsonl into long-term memory (memory.md) on a cron
schedule (default every 2h), creating a window where compressed content
is invisible to the LLM.

This change closes the gap by injecting unprocessed history entries
(history.jsonl entries not yet consumed by Dream) directly into the
system prompt as "# Recent History".

Key design notes:
- Uses read_unprocessed_history(since_cursor=last_dream_cursor) so only
  entries not yet reflected in long-term memory are included, avoiding
  duplication with memory.md
- No overlap with session messages: Consolidator advances
  last_consolidated before returning, so archived messages are already
  removed from get_history() output
- Token-safe: Consolidator's estimate_session_prompt_tokens calls
  build_system_prompt via the same build_messages function, so the
  injected entries are included in token budget calculations and will
  trigger further consolidation if needed

Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2026-04-07 23:41:05 +08:00
Xubin Ren
acafcf3cb0 docs: fix inaccurate claim about supports_streaming and dict config
supports_streaming already handles dict configs via isinstance check;
only is_allowed() fails with plain dicts. Narrow the explanation.

Made-with: Cursor
2026-04-07 23:01:30 +08:00
invictus
4648cb9e87 docs: use model_dump(by_alias=True) for default_config in plugin guide 2026-04-07 23:01:30 +08:00
invictus
83ad013be5 docs: fix channel plugin guide — require Pydantic config model 2026-04-07 23:01:30 +08:00
Xubin Ren
1e8a6663ca test(anthropic): add regression tests for thinking modes incl. adaptive
Also update schema comment to mention 'adaptive' as a valid value.

Made-with: Cursor
2026-04-07 22:53:43 +08:00
Balor.LC3
1c2f4aba17 feat(anthropic): add adaptive thinking mode
Extends reasoning_effort to accept 'adaptive' in addition to
low/medium/high. When set, uses Anthropic's type: 'adaptive'
thinking API instead of a fixed budget, letting the model decide
when and how much to think per turn.

Also auto-enables interleaved thinking between tool calls on
claude-sonnet-4-6 and claude-opus-4-6.

Usage:
  "reasoning_effort": "adaptive" in agents.defaults config
2026-04-07 22:53:43 +08:00
Xubin Ren
423aab09dd test(cron): add regression test for running service picking up external adds
Co-authored-by: chengyongru
Made-with: Cursor
2026-04-07 22:48:40 +08:00
xinnan.hou
a982d9f9be add reload jobs test 2026-04-07 22:48:40 +08:00
xinnan.hou
fd2bb3bb7d fix comment 2026-04-07 22:48:40 +08:00
xinnan.hou
4e914d0e2a fix not reload job config 2026-04-07 22:48:40 +08:00
chengyongru
b4f985f3dc feat(memory):dream enhancement (#2887)
* feat(dream): enhance memory cleanup with staleness detection

- Phase 1: add [FILE-REMOVE] directive and staleness patterns (14-day
  threshold, completed tasks, superseded info, resolved tracking)
- Phase 2: add explicit cleanup rules, file paths section, and deletion
  guidance to prevent LLM path confusion
- Inject current date and file sizes into Phase 1 context for age-aware
  analysis
- Add _dream_debug() helper for observability (dream-debug.log in workspace)
- Log Phase 1 analysis output and Phase 2 tool events for debugging

Tested with glm-5-turbo: MEMORY.md reduced from 149 to 108-129 lines
across two rounds, correctly identifying and removing weather data,
detailed incident info, completed research, and stale discussions.

* refactor(dream): replace _dream_debug file logger with loguru

Remove the custom _dream_debug() helper that wrote to dream-debug.log
and use the existing loguru logger instead. Phase 1 analysis is logged
at debug level, tool events at info level — consistent with the rest
of the codebase and no extra log file to manage.

* fix(dream): make stale scan independent of conversation history

Reframe Phase 1 from a single comparison task to two independent
tasks: history diff AND proactive stale scan. The LLM was skipping
stale content that wasn't referenced in conversation history (e.g.
old triage snapshots). Now explicitly requires scanning memory files
for staleness patterns on every run.

* fix(dream): correct old_text param name and truncate debug log

- Phase 2 prompt: old_string -> old_text to match EditFileTool interface
- Phase 1 debug log: truncate analysis to 500 chars to avoid oversized lines

* refactor(dream): streamline prompts by separating concerns

Phase 1 owns all staleness judgment logic; Phase 2 is pure execution
guidance. Remove duplicated cleanup rules from Phase 2 since Phase 1
already determines what to add/remove. Fix remaining old_string -> old_text.
Total prompt size reduced ~45% (870 -> 480 tokens).

* fix(dream): add FILE-REMOVE execution guidance to Phase 2 prompt

Phase 2 was only processing [FILE] additions and ignoring [FILE-REMOVE]
deletions after the cleanup rules were removed. Add explicit mapping:
[FILE] → add content, [FILE-REMOVE] → delete content.
2026-04-07 22:39:47 +08:00
Xubin Ren
82dec12f66 refactor: extract tool hint formatting to utils/tool_hints.py
- Move _tool_hint implementation from loop.py to nanobot/utils/tool_hints.py
- Keep thin delegation in AgentLoop._tool_hint for backward compat
- Update test imports to test format_tool_hints directly

Made-with: Cursor
2026-04-07 15:15:07 +08:00
chengyongru
3e3a7654f8 fix(agent): address code review findings for tool hint enhancement
- C1: Fix IndexError on empty list arguments via _get_args() helper
- I1: Remove redundant branch in _fmt_known
- I2: Export abbreviate_path from nanobot.utils.__init__
- I3: Fix _abbreviate_url negative-budget format consistency
- S1: Move FORMATS to class-level _TOOL_HINT_FORMATS constant
- S2: Add list_dir to FORMATS registry (ls path)
- G1-G5: Add tests for empty list args, None args, URL edge cases,
  mixed folding groups, and list_dir format
2026-04-07 15:15:07 +08:00
chengyongru
b1d3c00deb test(feishu): add compatibility tests for new tool hint format 2026-04-07 15:15:07 +08:00
chengyongru
238a9303d0 test: update tool_hint assertion to match new format 2026-04-07 15:15:07 +08:00
chengyongru
8ca9960077 feat(agent): rewrite _tool_hint with registry, path abbreviation, and call folding 2026-04-07 15:15:07 +08:00
chengyongru
f452af6c62 feat(utils): add abbreviate_path for smart path/URL truncation 2026-04-07 15:15:07 +08:00
Xubin Ren
02597c3ec9 fix(runner): silent retry on empty response before finalization 2026-04-07 15:03:41 +08:00
Xubin Ren
0355f20919 test: add regression tests for _resolve_mentions
7 tests covering: single mention, dual IDs, no-id skip, multiple mentions,
no mentions, empty text, and key-not-in-text edge case.

Made-with: Cursor
2026-04-07 14:03:55 +08:00
wudongxue
b3294f79aa fix(feishu): ensure access token is initialized before fetching bot open_id
The lark-oapi client requires token types to be explicitly configured
so that the SDK can obtain and attach the tenant_access_token to raw
requests. Without this, `_fetch_bot_open_id()` would fail with
"Missing access token for authorization" because the token had not
been provisioned at the time of the call.
2026-04-07 14:03:55 +08:00
wudongxue
0291d1f716 feat: resolve mentions data 2026-04-07 14:03:55 +08:00
Xubin Ren
075bdd5c3c refactor: move SafeFileHistory to module level + add regression tests
- Promote _SafeFileHistory to module-level SafeFileHistory for testability
- Add 5 regression tests: surrogates, normal text, emoji, mixed CJK, multi-surrogates

Made-with: Cursor
2026-04-07 13:57:34 +08:00
bahtya
64bd7234b3 fix(cli): sanitize surrogate characters in prompt history to prevent UnicodeEncodeError
On Windows, certain Unicode input (emoji, mixed-script text, surrogate
pairs) causes prompt_toolkit's FileHistory to crash with
UnicodeEncodeError when writing the history file.

Fix: wrap FileHistory with a _SafeFileHistory subclass that sanitizes
surrogate characters before writing, replacing invalid sequences instead
of crashing.

Fixes #2846
2026-04-07 13:57:34 +08:00
flobo3
67e6f8cc7a fix(docker): strip Windows CRLF from entrypoint.sh 2026-04-07 13:32:01 +08:00
Jiajun Xie
5ee96721f7 ci: add ruff lint check for unused imports and variables
Add CI step to detect unused imports (F401) and unused variables (F841)
with ruff. Clean up existing violations:

- Remove unused Consolidator import in agent/__init__.py
- Remove unused re import in agent/loop.py
- Remove unused Path import in channels/feishu.py
- Remove unused ContentRepositoryConfigError import in channels/matrix.py
- Remove unused field and CommandHandler imports in channels/telegram.py
- Remove unused exception variable in channels/weixin.py
2026-04-07 13:30:49 +08:00
04cb
f4904c4bdf fix(cron): add optional name parameter to separate job label from message (#2680) 2026-04-07 13:22:20 +08:00
Leo fu
44c7992095 fix(filesystem): correct write success message from bytes to characters
len(content) counts Unicode code points, not UTF-8 bytes. For non-ASCII
content such as Chinese or emoji, the reported count would be lower than
the actual bytes written to disk, which is misleading to the agent.
2026-04-07 13:22:00 +08:00
bahtya
cefeddab8e fix(matrix): correct e2eeEnabled camelCase alias mapping
The pydantic to_camel function generates 'e2EeEnabled' (treating 'ee'
as a word boundary) for the field 'e2ee_enabled'. Users writing
'e2eeEnabled' in their config get the default value instead.

Fix: add explicit alias='e2eeEnabled' to override the incorrect
auto-generated alias. Both 'e2eeEnabled' and 'e2ee_enabled' now work.

Fixes #2851
2026-04-07 13:20:55 +08:00
Xubin Ren
bf459c7887 fix(docker): fix volume mount path and add permission error guidance 2026-04-06 13:15:40 +00:00
Xubin Ren
4dac0a8930 docs: update nanobot docs badge 2026-04-06 11:55:47 +00:00
Xubin Ren
a30e84bfd1 docs: update v0.1.5 release news 2026-04-06 11:46:16 +00:00
Xubin Ren
6269876bc7 docs: update v0.1.5 release news 2026-04-06 11:45:37 +00:00
Xubin Ren
bc2253c83f docs: update v0.1.5 release news 2026-04-06 11:45:08 +00:00
Xubin Ren
b719da7400 fix(feishu): use RawRequest for bot info API 2026-04-06 11:39:23 +00:00
Xubin Ren
79234d237e chore: bump version to 0.1.5 v0.1.5 2026-04-06 11:26:07 +00:00
Xubin Ren
1243c08745 docs: update news section 2026-04-06 11:22:20 +00:00
Xubin Ren
dad9c07843 fix(tests): update Tavily usage tests to match actual API response shape
The _parse_tavily_usage implementation was updated to use the real
{account: {plan_usage, plan_limit, ...}} structure, but the tests
still used the old flat {used, limit, breakdown} format.

Made-with: Cursor
2026-04-06 19:17:55 +08:00
yanghan-cyber
e528e6dd96 fix(status): parse actual Tavily API response structure
The Tavily /usage endpoint returns a nested "account" object with
plan_usage/plan_limit/search_usage/etc fields, not the flat structure
with used/limit/breakdown that was assumed. This caused all usage
values to be None.
2026-04-06 19:17:55 +08:00
yanghan-cyber
84f0571e0d fix(status): use correct AgentLoop attribute for web search config
The /status command tried to access web search config via
`loop.config.tools.web.search`, but AgentLoop has no `config` attribute.
This caused the search usage lookup to silently return None, so web
search provider usage was never displayed.

Fix: use `loop.web_config.search` which is the actual attribute
set during AgentLoop.__init__.
2026-04-06 19:17:55 +08:00
Xubin Ren
f65f788ab1
Merge PR #2762: fix: make app-layer retry classification structured
fix: make app-layer retry classification structured (408/409/timeout/connection)
2026-04-06 16:47:49 +08:00