863 Commits

Author SHA1 Message Date
Xubin Ren
43a58335f6 fix(provider): narrow DeepSeek reasoning history cleanup
Made-with: Cursor
2026-05-01 19:52:38 +08:00
Xubin Ren
e157392250 fix(agent): scope subagent reply dedupe to origin message
Made-with: Cursor
2026-05-01 11:47:24 +00:00
yorkhellen
08f326ec55 test: Add tests for sender_id runtime context injection 2026-05-01 19:43:38 +08:00
hanyuanling
1040124ede Fix API stream lifecycle for tool-backed requests 2026-05-01 19:42:52 +08:00
hinotoi-agent
ad952e0da2 fix(dingtalk): block SSRF in outbound media fetches 2026-05-01 19:31:45 +08:00
copilot-swe-agent[bot]
0284174df9 fix: prevent empty Matrix messages when progress callback sends empty content
Agent-Logs-Url: https://github.com/halldorjanetzko/nanobot/sessions/df528c59-8214-41a0-9b79-9d1d41857107

Co-authored-by: halldorjanetzko <158819146+halldorjanetzko@users.noreply.github.com>
2026-05-01 19:31:04 +08:00
coldxiangyu
15007afd4a fix(matrix): skip events received before bot startup
Matrix sync replays the room timeline on each startup or `/restart`,
causing already-handled messages to be reprocessed (#3553). Even with
`store_sync_tokens=True`, the sync token isn't reliably re-injected
when restoring a session via access_token + load_store(), so the
client re-reads recent timeline entries.

Filter `event.server_timestamp` against the process start time so old
events are dropped at the `_on_message` / `_on_media_message` entry
points. Trade-off: messages received during downtime won't be
processed, which matches the issue reporter's expectation.

Closes #3553

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:30:33 +08:00
Xubin Ren
fae38319ca fix(tools): scope file state by session
Made-with: Cursor
2026-05-01 19:15:07 +08:00
LZDQ
58ae2d5b7e Claude: replace module-level file read states with per-loop per-session state class. fixes #3571 2026-05-01 19:15:07 +08:00
Xubin Ren
306958d6e6 add native Bedrock Converse provider
Made-with: Cursor
2026-05-01 18:52:03 +08:00
童天立
4e06c00b46 fix: add origin_message_id support for spawn and message deduplication 2026-04-30 21:22:48 +08:00
hanyuanling
3c20d16117 fix subagent max iteration limit 2026-04-30 13:45:40 +08:00
Xubin Ren
f8fd9f0011 fix(feishu): keep streaming replies in existing topics
Made-with: Cursor
2026-04-30 13:42:37 +08:00
hanyuanling
d82f25e4d4 fix(feishu): respect reply_to_message for group threads 2026-04-30 13:42:37 +08:00
Xubin Ren
26e953f0b9 Revert "fix(feishu): streaming card and tool hint respect reply_to_message in…"
This reverts commit 651b6b933f2db26713b5668d0c103d1b022e858c.
2026-04-30 13:27:37 +08:00
04cb
651b6b933f fix(feishu): streaming card and tool hint respect reply_to_message in groups 2026-04-30 12:51:08 +08:00
Xubin Ren
3d7099b421 fix(memory): clean atomic write test hygiene
Made-with: Cursor
2026-04-29 16:57:50 +08:00
yorkhellen
2af45945e2 fix(memory): ensure atomic write for history.jsonl
Use temp file + os.replace + fsync to prevent partial writes on crash.
Add tests for atomic write behavior and tmp file cleanup on exception.
2026-04-29 16:57:50 +08:00
chengyongru
74270bb8a8 refactor(channels): resolve progress overrides at init-time like transcription 2026-04-29 16:43:09 +08:00
hanyuanling
a0443e8f9e fix(channels): address progress override review 2026-04-29 16:43:09 +08:00
hanyuanling
0b111a0e0c fix(channels): support per-channel progress controls 2026-04-29 16:43:09 +08:00
masterlyj
2b9b41f9c3 test(providers): cover reasoning_effort="none" and gemma auto-routing
- Anthropic: "none" must not enable extended thinking
- Azure: "none" must not suppress temperature or inject reasoning body
- DeepSeek/DashScope/Kimi: "none" sends thinking disabled, skips reasoning_effort field
- Gemini: gemma keyword enables auto-routing for gemma models
2026-04-29 15:41:11 +08:00
chengyongru
28f9bbff31 feat(web_search): add olostep provider
Adds Olostep (https://www.olostep.com) as an optional web_search backend
using the official olostep Python SDK (client.answers.create()).

Changes:
- pyproject.toml: adds olostep>=0.1.0 optional dependency
- schema.py: adds olostep to provider comment in WebSearchConfig
- web.py: adds _search_olostep() with lazy import and provider branching
- docs/configuration.md: documents Olostep setup under web search config
- tests: unit tests for the new provider

Backward compatible: existing users see no behavior change unless they
opt into provider: "olostep". No hard dependency at runtime path.

Co-authored-by: umerkay <umerkk164@gmail.com>
2026-04-28 19:09:38 +08:00
甘全
0053e68423 fix(feishu): skip reaction transition on resuming stream end
Stream-end events are emitted at the end of every assistant turn. When
the agent has more tool-call rounds queued, the runner sets
`_resuming=True` on the metadata. Without a guard, every intermediate
stream end removed the OnIt reaction (the first one wins, since
`_reaction_ids.pop` empties the slot) and re-added `done_emoji`,
producing a DONE reaction after every tool call instead of only at
final completion.

Wrap the OnIt removal and `done_emoji` add in a `not _resuming` guard
so the OnIt indicator persists across tool-call rounds and DONE fires
exactly once when the agent's final response lands.

`_resuming` already flows through outbound metadata
(`nanobot/agent/loop.py:747`) and survives `_coalesce_stream_deltas`
because pure `_stream_end` messages without `_stream_delta` skip the
merge branch.

Tests:
- test_no_removal_when_resuming
- test_done_emoji_only_on_final_stream_end
2026-04-28 17:29:12 +08:00
hussein1362
415e617398 feat(providers): add extra_body config for OpenAI-compatible endpoints
Add an `extra_body` field to `ProviderConfig` that merges arbitrary
key-value pairs into every OpenAI-compatible request body. This is the
escape hatch for provider-specific features that nanobot does not have
first-class fields for.

Real-world use cases this unblocks via config alone (no code changes):
- vLLM/TGI `chat_template_kwargs` (e.g. `enable_thinking: false`)
- vLLM guided decoding (`guided_json`, `guided_regex`)
- Local model sampling params (`repetition_penalty`, `top_k`, `min_p`)
- Any future provider-specific param without a new PR each time

The config extra_body is applied last via recursive deep-merge, so it
can extend or override provider-specific defaults (e.g. thinking
params) without clobbering sibling keys set by internal logic.

Changes:
- Add `extra_body: dict[str, Any] | None` to `ProviderConfig`
- Pass it through `factory.py` to `OpenAICompatProvider.__init__`
- Deep-merge into `_build_kwargs` after all internal extra_body entries
- Add `_deep_merge` helper (recursive dict merge, does not mutate inputs)
- 21 tests: deep-merge semantics, provider init, _build_kwargs
  integration, thinking coexistence, real-world patterns (guided_json,
  repetition_penalty), and schema validation
2026-04-28 15:56:13 +08:00
Xubin Ren
f4d8783f5e test(web): cover configurable fetch behavior
Ensure custom user agents are applied to direct web requests and disabling Jina Reader forces the local readability path.

Made-with: Cursor
2026-04-28 07:25:47 +00:00
Xubin Ren
50698c3d1c test(telegram): cover local attachment filenames
Add a regression test for preserving the original basename when Telegram sends local media bytes.

Made-with: Cursor
2026-04-28 15:13:49 +08:00
Xubin Ren
48f3cc6390 fix(agent): stop on workspace violations from tool errors
Treat workspace and safety guard failures as fatal regardless of whether they arrive from tool preparation, returned tool output, or raised exceptions.

Made-with: Cursor
2026-04-28 15:13:27 +08:00
Xubin Ren
ad4802600e refactor(config): make max messages default explicit
Use 120 as the config-level default and normalize zero back to that limit so session replay always receives an explicit message cap.

Made-with: Cursor
2026-04-28 14:54:32 +08:00
hussein1362
d45ffcf519 feat(config): wire max_messages into session history replay
The max_messages config field in AgentDefaults was accepted by the
schema but never threaded through to the actual get_history() calls
in the agent loop.  Both call sites in _process_message hardcoded the
default, so sessions with slow or local models accumulated unbounded
history that inflated prompt tokens and caused LLM timeouts.

Changes:
- Add max_messages field to AgentDefaults (default 0 = use built-in
  constant, any positive value caps history replay)
- Store the value on AgentLoop and pass it to get_history() when
  non-zero
- Wire the config through all three AgentLoop construction sites in
  commands.py (gateway, API server, CLI chat)
- 14 focused tests covering schema validation, init storage, history
  slicing, boundary alignment, integration wiring, and the
  zero/default path
2026-04-28 14:54:32 +08:00
Xubin Ren
fdfecd3ba6 refactor(codex): name progress delta capability semantically
Use a provider capability name that describes user-visible progress delta support instead of the runner implementation detail.

Made-with: Cursor
2026-04-27 18:48:05 +08:00
hanyuanling
ae14142a87 fix(codex): stream progress deltas to channels 2026-04-27 18:48:05 +08:00
Xubin Ren
2b886ffd1f fix(command): expose history in chat command menus
Made-with: Cursor
2026-04-27 18:23:35 +08:00
Xubin Ren
8ed10ac7df test(command): keep history tests lint-clean
Made-with: Cursor
2026-04-27 18:23:35 +08:00
Leo fu
599e25dfbf feat(command): add /history command to show recent session messages
Adds /history [n] to display the last N user/assistant messages from
the current session (default 10, max 50).

- Tool and system messages are filtered out for readability
- Long messages are truncated to 200 characters with an ellipsis
- Multimodal content (image blocks) is collapsed to its text parts
- Invalid count argument returns a usage hint
- /history n uses prefix routing; /history uses exact routing

Also registers /history in build_help_text().
2026-04-27 18:23:35 +08:00
hussein1362
e72c415473 fix(heartbeat): prevent internal reasoning leaks and finalization fallback in delivery
Three failure modes addressed:

1. Model reflects HEARTBEAT.md instructions back as output instead of
   executing them ("HEARTBEAT.md has active tasks listed...")
2. Model narrates decision logic ("Best judgment call: stay quiet")
3. Model produces empty output for silence, runner treats it as failure,
   finalization retry generates "couldn't produce a final answer" which
   gets delivered to the user

Changes:
- Add _is_deliverable() pre-filter in HeartbeatService._tick() that catches
  finalization fallback messages and leaked reasoning patterns before they
  reach the evaluator
- Wrap Phase 2 task input with a delivery-awareness preamble telling the
  model its output goes directly to the user's messaging app
- Add meta-reasoning suppression criterion to evaluator template

No changes to agent/loop.py, runner.py, providers, or config schema.
2026-04-27 18:14:13 +08:00
hanyuanling
9dc99d1b34 fix(provider): bound OpenAI-compatible request timeouts 2026-04-27 17:47:31 +08:00
Xubin Ren
e31273ebaa Merge origin/main into fix/discord-allow-channel-threads
Made-with: Cursor
2026-04-27 09:26:24 +00:00
Xubin Ren
82c5083b15 fix(slack): preserve DM thread routing and strip trailing newlines 2026-04-27 09:01:04 +00:00
Xubin Ren
2fe8d21b6e
Merge PR #3459: feat(session): enforce replay/file-cap invariants for history lifecycle
feat(session): enforce replay/file-cap invariants for history lifecycle
2026-04-27 16:17:23 +08:00
Xubin Ren
eb4b3d9e26 refactor(session): internalize history/file-cap knobs as constants
Move sessionHistoryMaxMessages, sessionHistoryMaxTokens, and
sessionFileMaxMessages out of user-facing config into internal
constants (HISTORY_MAX_MESSAGES=120, FILE_MAX_MESSAGES=2000).

- Remove 3 fields from AgentDefaults and config pipeline
- Sink enforce_file_cap into Session (was AgentLoop)
- Auto-derive token budget from context window (was configurable)
- Net -113 lines across 7 files; 723 tests green

Made-with: Cursor
2026-04-27 08:06:50 +00:00
Xubin Ren
537c66a3f8
Merge PR #3440: fix: Automatically clean up unsupported or expired MSTeams session
fix: Automatically clean up unsupported or expired MSTeams session
2026-04-27 15:45:31 +08:00
hanyuanling
8e0ce59c0e fix(provider): normalize DeepSeek non-string message content 2026-04-27 15:43:41 +08:00
Xubin Ren
29ebc2d355 Merge origin/main into feat/session-replay-file-cap-invariants
Preserve main's timestamp/tool-context replay semantics while keeping the PR's session history and file-cap budgets.

Made-with: Cursor
2026-04-27 07:32:00 +00:00
Xubin Ren
367a6db78c test(msteams): align stale-ref test with sidecar metadata
The PR stores ref freshness in the metadata sidecar, so the merged main test should assert updated_at there instead of in the refs payload.

Made-with: Cursor
2026-04-27 07:30:17 +00:00
Xubin Ren
3d75aedcac Merge origin/main into fix/msteams-prune-stale-refs
Resolve the MSTeams stale-reference cleanup conflict by keeping the PR's locked, atomic sidecar-meta implementation and aligning the merged test expectation locally.

Made-with: Cursor
2026-04-27 07:29:48 +00:00
Xubin Ren
311a7fe36e fix(session): stop training the model to parrot [Message Time: ...]
Past assistant turns in history were prefixed with "[Message Time: ...]"
just like user turns. The model treated these as in-context demos and
started prefixing its own replies with the same marker, leaking
metadata to the user. Prompt-level warnings could not beat dozens of
prior assistant samples.

Annotate only user turns and proactive deliveries
(_channel_delivery=True, i.e. cron / heartbeat pushes whose timing is
the whole point and which are too infrequent to act as demos). Adjacent
user-side timestamps still pin every normal assistant reply for
relative-time reasoning. The now-redundant identity.md warning is
removed along with the demonstration source.
2026-04-27 07:11:20 +00:00
Xubin Ren
620d9e4f31 fix(slack): accept inbound file_share messages without dropping them
Slack inbound events with subtype=file_share were silently dropped, so
nanobot never saw messages that included attachments. Allow file_share
through, download Slack-private files using the bot token into the
local media dir, and pass them to the agent as media paths plus a
"[file: name]" / "[image: name]" placeholder in the content. Reject
responses that look like Slack's login HTML so an auth page is never
saved as if it were the user's file. Document the required files:read
scope alongside files:write so installs that read attachments are not
quietly missing the permission.
2026-04-27 07:11:11 +00:00
Xubin Ren
7dcf83e389 test(agent): cover threaded subagent routing
Made-with: Cursor
2026-04-27 14:37:36 +08:00
Xubin Ren
9b6f3d7abc fix(agent): resolve message media against active workspace
Made-with: Cursor
2026-04-27 14:31:39 +08:00