Rename the boolean flag in _sanitize_persisted_blocks and alias the imported helper so session persistence cannot crash with TypeError when truncation is enabled.
Keep tool-call assistant messages valid across provider sanitization and avoid trailing user-only history after model errors. This prevents follow-up requests from sending broken tool chains back to the gateway.
- Wrap both token estimation calls in try/except to prevent silent failures
from crashing the consolidation cycle
- Add _MAX_CHUNK_MESSAGES = 60 to cap messages per consolidation round,
avoiding oversized chunks being sent to the consolidation LLM
- Improve idle log to include unconsolidated message count for easier debugging
These are purely defensive improvements with no behaviour change for
normal sessions.
- Adjusted message handling in AgentRunner to ensure that historical messages remain unchanged during context governance.
- Introduced tests to verify that backfill operations do not alter the saved message boundary, maintaining the integrity of the conversation history.
- Make tool_hint_prefix configurable in FeishuConfig (default: 🔧)
- Delegate tool hint card updates from send() to send_delta() so hints
automatically benefit from _STREAM_EDIT_INTERVAL throttling
- Fix staticmethod calls to use self.__class__ instead of self
- Document all supported metadata keys in send_delta docstring
- Add test for empty/whitespace-only tool hint with active stream buffer
Two display fixes based on real-world Feishu testing:
1. tool_hints.py: format_tool_hints now deduplicates by comparing the
fully formatted hint string instead of tool name alone. This fixes
`ls /Desktop` and `ls /Downloads` being incorrectly merged as
`ls /Desktop × 2`. Truly identical calls still fold correctly.
(_group_consecutive and all abbreviation logic preserved unchanged.)
2. feishu.py: inline tool hints now display one tool per line with
🔧 prefix, and use double-newline trailing to prevent Setext heading
rendering when followed by markdown `---`.
Made-with: Cursor
Tool hints should be kept as permanent content in the streaming card
so users can see which tools were called (matching the standalone card
behavior). Previously, hints were stripped when new deltas arrived or
when the stream ended, causing tool call information to disappear.
Now:
- New delta: hint becomes permanent content, delta appends after it
- New tool hint: replaces the previous hint (unchanged)
- Resuming/stream_end: hint is preserved in the final text
Updated 3 tests to verify hint preservation semantics.
Made-with: Cursor
Three fixes for inline tool hints:
1. Consecutive tool hints now replace the previous one instead of
stacking — the old suffix is stripped before appending the new one.
2. When _resuming flushes the buffer, any trailing tool hint suffix
is removed so it doesn't persist into the next streaming segment.
3. When final _stream_end closes the card, tool hint suffix is
cleaned from the text before the final card update.
Adds 3 regression tests covering all three scenarios.
Made-with: Cursor
Two improvements to Feishu streaming card experience:
1. Handle _resuming in send_delta: when a mid-turn _stream_end arrives
with resuming=True (tool call between segments), flush current text
to the card but keep the buffer alive so subsequent segments append
to the same card instead of creating a new one.
2. Inline tool hints into streaming cards: when a tool hint arrives
while a streaming card is active, append it to the card content
(e.g. "🔧 web_fetch(...)") instead of sending a separate card.
The hint is automatically stripped when the next delta arrives.
Made-with: Cursor
- Merged latest main (no conflicts)
- Added warning log when only one of proxy_username/proxy_password is set
- Added test_start_no_proxy_auth_when_only_password for coverage parity
Made-with: Cursor
- Add proxy, proxy_username, proxy_password fields to DiscordConfig
- Pass proxy and proxy_auth to discord.Client
- Add aiohttp.BasicAuth when credentials are provided
- Add tests for proxy configuration scenarios
Add allowed_env_keys config field to selectively forward host environment variables (e.g. GOPATH, JAVA_HOME) into the sandboxed subprocess environment, while keeping the default allow-list unchanged.
- Merged latest main (no conflicts)
- Added test_llm_error_not_appended_to_session_messages: verifies error
content stays out of session messages
- Added test_streamed_flag_not_set_on_llm_error: verifies _streamed is
not set when LLM returns an error, so ChannelManager delivers it
Made-with: Cursor
When the LLM returns an error (e.g. 429 quota exceeded, stream timeout),
streaming channels silently drop the error message because `_streamed=True`
is set in metadata even though no content was actually streamed.
This change:
- Skips setting `_streamed` when stop_reason is "error", so error messages
go through the normal channel.send() path and reach the user
- Stops appending error content to session history, preventing error
messages from polluting subsequent conversation context
- Exposes stop_reason from _run_agent_loop to enable the above check
- Use hmac.compare_digest for timing-safe static token comparison
- Add issued token capacity limit (_MAX_ISSUED_TOKENS=10000) with 429 response
- Use atomic pop in _take_issued_token_if_valid to eliminate TOCTOU window
- Enforce TLSv1.2 minimum version for SSL connections
- Extract _safe_send helper for consistent ConnectionClosed handling
- Move connection registration after ready send to prevent out-of-order delivery
- Add HTTP-level allow_from check and client_id truncation in process_request
- Make stop() idempotent with graceful shutdown error handling
- Normalize path via validator instead of leaving raw value
- Default websocket_requires_token to True for secure-by-default behavior
- Add integration tests and ws_test_client helper
- Refactor tests to use shared _ch factory and bus fixture
Resolved conflict in azure_openai_provider.py by keeping main's
Responses API implementation (role alternation not needed for the
Responses API input format).
Made-with: Cursor
The Enabled column in channels status and plugins list commands had a default green style that overrode the dim markup for disabled items. This caused no values to appear green instead of dimmed. Remove the default style to let cell-level markup control the display correctly.
Fix accidental line corruption in split_message() where 'break' was
merged with unrelated code during manual editing.
The actual fix: build_assistant_message() now returns content or ""
instead of content (which could be None), preventing providers like
MiMo V2 Omni from rejecting tool-call messages with missing text field.
Fixes#2519
nanobot's Windows exec environment was not forwarding ProgramFiles and related variables, so docker desktop start could not discover the desktop CLI plugin and reported unknown command. Forward the missing variables and add a regression test that covers the Windows env shape.
The /status command divided context_used by 1000 but context_total by
1024, producing inconsistent values. For example a 128000-token window
displayed as 125k instead of 128k. Tokens are not a binary unit, so
both should use 1000.
Keep importlib.metadata as the primary source for installed packages, but avoid PackageNotFoundError when nanobot is imported directly from a source tree.
Made-with: Cursor
Fixes#2856
Previously __version__ was hardcoded as '0.4.1' in __init__.py while
pyproject.toml declared version '0.1.5'. This caused nanobot gateway to
report version 0.4.1 on startup while pip showed 0.1.5.
Now __version__ reads from importlib.metadata.version('nanobot-ai'),
keeping pyproject.toml as the single source of truth.
Add reraise parameter to AgentHook so hooks can opt out of exception
swallowing in CompositeHook._for_each_hook_safe. _LoopHook sets
reraise=True to let its exceptions propagate. _LoopHookChain is removed
and replaced with CompositeHook([loop_hook] + extra_hooks).
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
Keep manual runs from flipping the scheduler's running flag, rebuild merged run history records from action logs, and avoid delaying sub-second jobs to a one-second floor. Add regression coverage for disabled/manual runs, merged history persistence, and sub-second timers.
Made-with: Cursor
Preserve path folding for quoted exec command paths with spaces so hint previews do not fall back to mid-path truncation. Add regression coverage for quoted Unix and Windows path cases.
Made-with: Cursor