Commit Graph
330 Commits
Author SHA1 Message Date
Xubin RenandXubin Ren 13d6c0ae52 feat(config): add configurable timezone for runtime context
Add agent-level timezone configuration with a UTC default, propagate it into runtime context and heartbeat prompts, and document valid IANA timezone usage in the README.
2026-03-25 22:07:14 +08:00
chengyongruandXubin Ren 72acba5d27 refactor(tests): optimize unit test structure 2026-03-24 15:12:22 +08:00
Xubin RenandXubin Ren d25985be0b fix(filesystem): clarify optional tool argument handling
Keep the mypy-friendly optional execute signatures while returning clearer errors for missing arguments and locking that behavior with regression tests.

Made-with: Cursor
2026-03-24 11:49:10 +08:00
19emtuckandXubin Ren d4a7194c88 remove some none used f string 2026-03-24 11:49:10 +08:00
19emtuckandXubin Ren 69f1dcdba7 proposal to adopt mypy some e.g. interfaces problems 2026-03-24 11:49:10 +08:00
8abbe8a6df fix(agent): instruct LLM to use message tool for file delivery
During testing, we discovered that when a user requests the agent to
send a file (e.g., "send me IMG_1115.png"), the agent would call
read_file to view the content and then reply with text claiming
"file sent" — but never actually deliver the file to the user.

Root cause: The system prompt stated "Reply directly with text for
conversations. Only use the 'message' tool to send to a specific
chat channel", which led the LLM to believe text replies were
sufficient for all responses, including file delivery.

Fix: Add an explicit IMPORTANT instruction in the system prompt
telling the LLM it MUST use the 'message' tool with the 'media'
parameter to send files, and that read_file only reads content
for its own analysis.

Co-Authored-By: qulllee <qullkui@tencent.com>
2026-03-24 01:11:33 +08:00
qullleeandXubin Ren bc9f861bb1 feat: add media message support in agent context and message tool
Cherry-picked from PR #2355 (ad128a7) — only agent/context.py and agent/tools/message.py.

Co-Authored-By: qulllee <qullkui@tencent.com>
2026-03-24 01:11:33 +08:00
Eric YangandXubin Ren 84a7f8af73 refactor(shell): fix syntax error 2026-03-24 00:02:49 +08:00
Eric YangandXubin Ren e2e1c9c276 refactor(shell): use finally block to reap zombie processes on timeoutx 2026-03-24 00:02:49 +08:00
Eric YangandXubin Ren dbcc7cb539 refactor(shell): use finally block to reap zombie processes on timeout 2026-03-24 00:02:49 +08:00
Eric YangandXubin Ren e423ceef9c fix(shell): reap zombie processes when command timeout kills subprocess 2026-03-24 00:02:49 +08:00
gem12andXubin Ren 97fe9ab7d4 feat(agent): replace global lock with per-session locks for concurrent dispatch
Replace the single _processing_lock (asyncio.Lock) with per-session locks
so that different sessions can process LLM requests concurrently, while
messages within the same session remain serialised.

An optional global concurrency cap is available via the
NANOBOT_MAX_CONCURRENT_REQUESTS env var (default 3, <=0 for unlimited).

Also re-binds tool context before each tool execution round to prevent
concurrent sessions from clobbering each other's routing info.

Tested in production and manually reviewed.

(cherry picked from commit c397bb4229e8c3b7f99acea7ffe4bea15e73e957)
2026-03-23 18:57:03 +08:00
Xubin RenandXubin Ren 20494a2c52 refactor command routing for future plugins and clearer CLI structure 2026-03-23 16:48:42 +08:00
Xubin RenandXubin Ren aba0b83a77 fix(memory): reserve completion headroom for consolidation
Trigger token consolidation before prompt usage reaches the full context window so response tokens and tokenizer estimation drift still fit safely within the model budget.

Made-with: Cursor
2026-03-23 11:54:44 +08:00
Xubin RenandXubin Ren 9d5e511a6e feat(streaming): centralize think-tag filtering and add Telegram streaming
- Add strip_think() to helpers.py as single source of truth
- Filter deltas in agent loop before dispatching to consumers
- Implement send_delta in TelegramChannel with progressive edit_message_text
- Remove duplicate think filtering from CLI stream.py and telegram.py
- Remove legacy fake streaming (send_message_draft) from Telegram
- Default Telegram streaming to true
- Update CHANNEL_PLUGIN_GUIDE.md with streaming documentation

Made-with: Cursor
2026-03-23 10:20:41 +08:00
Xubin RenandXubin Ren bd621df57f feat: add streaming channel support with automatic fallback
Provider layer: add chat_stream / chat_stream_with_retry to all providers
(base fallback, litellm, custom, azure, codex). Refactor shared kwargs
building in each provider.

Channel layer: BaseChannel gains send_delta (no-op) and supports_streaming
(checks config + method override). ChannelManager routes _stream_delta /
_stream_end to send_delta, skips _streamed final messages.

AgentLoop._dispatch builds bus-backed on_stream/on_stream_end callbacks
when _wants_stream metadata is set. Non-streaming path unchanged.

CLI: clean up spinner ANSI workarounds, simplify commands.py flow.
Made-with: Cursor
2026-03-23 10:20:41 +08:00
Xubin RenandXubin Ren e79b9f4a83 feat(agent): add streaming groundwork for future TUI
Preserve the provider and agent-loop streaming primitives plus the CLI experiment scaffolding so this work can be resumed later without blocking urgent bug fixes on main.

Made-with: Cursor
2026-03-23 10:20:41 +08:00
Xubin RenandXubin Ren 48c71bb61e refactor(agent): unify process_direct to return OutboundMessage
Merge process_direct() and process_direct_outbound() into a single
interface returning OutboundMessage | None. This eliminates the
dual-path detection logic in CLI single-message mode that relied on
inspect.iscoroutinefunction to distinguish between the two APIs.

Extract status rendering into a pure function build_status_content()
in utils/helpers.py, decoupling it from AgentLoop internals.

Made-with: Cursor
2026-03-22 00:39:38 +08:00
Xubin Ren e430b1daf5 fix(agent): refine status output and CLI rendering
Keep status output responsive while estimating current context from session history, dropping low-value queue/subagent counters, and marking command-style replies for plain-text rendering in CLI. Also route direct CLI calls through outbound metadata so help/status formatting stays explicit instead of relying on content heuristics.

Made-with: Cursor
2026-03-21 15:52:10 +00:00
Xubin Ren 4d1897609d fix(agent): make status command responsive and accurate
Handle /status at the run-loop level so it can return immediately while the agent is busy, and reset last-usage stats when providers omit usage data. Also keep Telegram help/menu coverage for /status without changing the existing final-response send path.

Made-with: Cursor
2026-03-21 15:21:32 +00:00
Xubin Ren 570ca47483 Merge branch 'main' into pr-1985 2026-03-21 09:48:09 +00:00
Xubin RenandXubin Ren e87bb0a82d fix(mcp): preserve schema semantics during normalization
Only normalize nullable MCP tool schemas for OpenAI-compatible providers so optional params still work without collapsing unrelated unions. Also teach local validation to honor nullable flags and add regression coverage for nullable and non-nullable schemas.

Made-with: Cursor
2026-03-21 14:35:47 +08:00
haosenwang1018andXubin Ren b6cf7020ac fix: normalize MCP tool schema for OpenAI-compatible providers 2026-03-21 14:35:47 +08:00
Xubin Ren 445a96ab55 fix(agent): harden multimodal tool result flow
Keep multimodal tool outputs on the native content-block path while
restoring redirect SSRF checks for web_fetch image responses. Also share
image block construction, simplify persisted history sanitization, and
add regression tests for image reads and blocked private redirects.

Made-with: Cursor
2026-03-21 05:34:56 +00:00
Xubin Ren 834f1e3a9f Merge branch 'main' into pr-2304 2026-03-21 04:14:40 +00:00
Xubin Ren 1c39a4d311 refactor(tools): keep exec enable without configurable deny patterns
Made-with: Cursor
2026-03-20 17:46:08 +00:00
Xubin Ren 3825ed8595 merge origin/main into pr-1824
- wire tools.exec.enable and deny_patterns into the current AgentLoop
- preserve the current WebSearchTool config-based registration path
- treat deny_patterns=[] as an explicit override instead of falling back
  to the default blacklist
- add regression coverage for disabled exec registration and custom deny
  patterns

Made-with: Cursor
2026-03-20 17:21:42 +00:00
vandazia 71a88da186 feat: implement native multimodal autonomous sensory capabilities 2026-03-20 22:00:38 +08:00
Xubin RenandXubin Ren aacbb95313 fix(agent): preserve external cancellation in message loop
Made-with: Cursor
2026-03-20 19:27:26 +08:00
cdkey85andXubin Ren d83ba36800 fix(agent): handle asyncio.CancelledError in message loop
- Catch asyncio.CancelledError separately from generic exceptions
- Re-raise CancelledError only when loop is shutting down (_running is False)
- Continue processing messages if CancelledError occurs during normal operation
- Prevents anyio/MCP cancel scopes from prematurely terminating the agent loop
2026-03-20 19:27:26 +08:00
Xubin RenandXubin Ren c138b2375b docs: refine spawn workspace guidance wording
Adjust the spawn tool description to keep the workspace-organizing hint while
avoiding language that sounds like the system automatically assigns a dedicated
working directory for subagents.

Made-with: Cursor
2026-03-20 13:30:21 +08:00
JilunSun7274andXubin Ren e5179aa7db delete redundant whitespaces in subagent prompts 2026-03-20 13:30:21 +08:00
JilunSun7274andXubin Ren 517de6b731 docs: add subagent workspace assignment hint to spawn tool description 2026-03-20 13:30:21 +08:00
0b1beb0e9f Fix TypeError for MCP tools with nullable JSON Schema params
MCP servers (e.g. Zapier) return JSON Schema union types like
`"type": ["string", "null"]` for nullable parameters. The existing
`validate_params()` and `cast_params()` methods expected only simple
strings as `type`, causing `TypeError: unhashable type: 'list'` on
every MCP tool call with nullable parameters.

Add `_resolve_type()` helper that extracts the first non-null type
from union types, and use it in `_cast_value()` and `_validate()`.
Also handle `None` values correctly when the schema declares a
nullable type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:13:11 +08:00
Xubin RenandXubin Ren 5bd1c9ab8f fix(cron): preserve exact intervals in list output 2026-03-18 12:39:06 +08:00
8d45fedce7 refactor(cron): extract _format_timing and _format_state helpers
Addresses review feedback: moves schedule formatting and state
formatting into dedicated static methods, removes duplicate
in-loop imports, and simplifies _list_jobs() to a clean loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:39:06 +08:00
228e1bb3de style: apply ruff format to cron tool
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:39:06 +08:00
eb83778f50 fix(cron): show schedule details and run state in _list_jobs() output
_list_jobs() only displayed job name, id, and schedule kind (e.g. "cron"),
omitting the actual timing and run state. The agent couldn't answer
"when does this run?" or "did it run?" even though CronSchedule and
CronJobState had all the data.

Now surfaces:
- Cron expression + timezone for cron jobs
- Human-readable interval for every jobs
- ISO timestamp for one-shot at jobs
- Enabled/disabled status
- Last run time + status (ok/error/skipped) + error message
- Next scheduled run time

Fixes #1496

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:39:06 +08:00
zhangxiaoyu.yorkandXubin Ren f72ceb7a3c fix:set subagent result message role = assistant 2026-03-18 00:43:46 +08:00
Xubin RenandXubin Ren 8cf11a0291 fix: preserve image paths in fallback and session history 2026-03-17 22:37:09 +08:00
Xubin Ren 46b19b15e1 perf: background post-response memory consolidation for faster replies 2026-03-16 09:01:11 +00:00
Xubin Ren 6d63e22e86 Merge remote-tracking branch 'origin/main' into pr-1961
Made-with: Cursor

# Conflicts:
#	.gitignore
2026-03-16 08:47:28 +00:00
Xubin RenandXubin Ren b29275a1d2 refactor(/new): background archival with guaranteed persistence
Replace fire-and-forget consolidation with archive_messages(), which
retries until the raw-dump fallback triggers — making it effectively
infallible. /new now clears the session immediately and archives in
the background. Pending archive tasks are drained on shutdown via
close_mcp() so no data is lost on process exit.
2026-03-16 16:40:09 +08:00
chengyongruandXubin Ren 9820c87537 fix(loop): restore /new immediate return with safe background consolidation
PR #881 (commit 755e424) fixed the race condition between normal consolidation
and /new consolidation, but did so by making /new wait for consolidation to
complete before returning. This hurts user experience - /new should be instant.

This PR restores the original immediate-return behavior while keeping safety:

1. **Immediate return**: Session clears and user sees "New session started" right away
2. **Background archival**: Consolidation runs in background via asyncio.create_task
3. **Serialized consolidation**: Uses the same lock as normal consolidation via
   `memory_consolidator.get_lock()` to prevent concurrent writes

If consolidation fails after session clear, archived messages may be lost.
This is acceptable because:
- User already sees the new session and can continue working
- Failure is logged for debugging
- The alternative (blocking /new on every call) hurts UX for all users
2026-03-16 16:40:09 +08:00
Xubin RenandXubin Ren 6e2b6396a4 security: add SSRF protection, untrusted content marking, and internal URL blocking 2026-03-16 15:05:26 +08:00
Xubin RenandXubin Ren 5d1528a5f3 fix(heartbeat): inject shared current time context into phase 1 2026-03-16 10:52:26 +08:00
Xubin RenandXubin Ren d684fec27a Replace load_skill tool with read_file extra_allowed_dirs for builtin skills access
Instead of adding a separate load_skill tool to bypass workspace restrictions,
extend ReadFileTool with extra_allowed_dirs so it can read builtin skill paths
while keeping write/edit tools locked to the workspace. Fixes the original issue
for both main agent and subagents.

Made-with: Cursor
2026-03-15 23:21:02 +08:00
BenandXubin Ren 45832ea499 Add load_skill tool to bypass workspace restriction for builtin skills
When restrictToWorkspace is enabled, the agent cannot read builtin skill
files via read_file since they live outside the workspace. This adds a
dedicated load_skill tool that reads skills by name through the SkillsLoader,
which accesses files directly via Python without the workspace restriction.

- Add LoadSkillTool to filesystem tools
- Register it in the agent loop
- Update system prompt to instruct agent to use load_skill instead of read_file
- Remove raw filesystem paths from skills summary
2026-03-15 23:21:02 +08:00
Xubin Ren 19ae7a167e fix(feishu): avoid breaking tool hint formatting and think stripping 2026-03-14 15:40:53 +00:00
Xubin Ren 44af7eca3f merge: resolve PR #1966 conflicts with main 2026-03-14 15:32:19 +00:00