Commit Graph
3094 Commits
Author SHA1 Message Date
chengyongruandXubin Ren 8e9f09f829 fix: keep onboard cli defaults unchanged
Remove the PR changes that made nanobot onboard default to the wizard, added --defaults, added non-TTY fallback behavior, and changed the Docker smoke command. Keep Quick Start available through nanobot onboard --wizard.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren da225fc24e fix: fetch openai quick start models
Use OpenAI's SDK default base URL for Quick Start model discovery without writing that default into the saved provider config.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 71631f9ab0 fix: ask provider in quick start
Maintainer edit: replace Quick Start key/base detection with an explicit provider-first flow. Users choose the provider that issued the API key, paste the key, and only custom OpenAI-compatible setups ask for a base URL.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 2319b660e6 fix: make quick start provider neutral
Maintainer edit: restart the Quick Start flow around an API-key-first path without recommending OpenRouter or DeepSeek. Detect unique key prefixes locally, fall back to a user-provided base URL, and only fetch models from that approved URL.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren ed3a8f64d8 fix: use deepseek for quick start onboarding
Maintainer edit: restart the Quick Start default around a mainland-friendly provider instead of making OpenRouter the first-run dependency. Update wizard copy, default preset, focused tests, and beginner docs to use DeepSeek with the current deepseek-v4-flash model.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 0e9b136315 fix: streamline quick start onboarding flow
Maintainer edit: continue the wizard simplification pass by making Quick Start save after the API key path, hiding save/summary actions until they are needed, removing failed-key side effects, and aligning beginner docs with the local WebUI path.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren fa5f7f5b88 fix: make onboarding api-key first
Maintainer edit: collapse the first-run wizard to an API-key-only Quick Start and move lower-frequency provider, model, channel, gateway, and tool settings behind Advanced Settings.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 1dbec3da50 fix: simplify beginner quick start
Maintainer edit: reduce the default onboarding path to a recommended local WebUI setup that only asks for an OpenRouter key, while keeping the detailed provider/channel flow available for advanced setup.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 84143d31b2 refactor: reuse onboard config refresh path
Simplify pass: share the existing-config refresh flow between non-interactive defaults and declined overwrite prompts without changing behavior.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 3d773d9054 fix: preserve config during non-interactive defaults onboarding
Maintainer edit: make explicit --defaults refresh existing configs without prompting when no TTY is available, preserving user values for CI and Docker runs.
2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 9c7d1c9507 Use channel login in onboarding wizard 2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren db68fc1c09 Use provider registry in quick start 2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren f9f5a19910 Route WebUI quick start through channel config 2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren 0097488eed Keep quick start channels opt-in 2026-06-22 13:04:05 +08:00
chengyongruandXubin Ren fc7971b3b6 Improve onboard wizard setup flow 2026-06-22 13:04:05 +08:00
Xubin Ren 9db3dc5e32 docs(readme): update news through 2026-06-20 2026-06-21 17:14:53 +08:00
Xubin Ren dbf3c4b245 feat(sdk): expand Python runtime controls 2026-06-21 16:55:23 +08:00
Xubin Ren f4cc001410 refactor(sdk): pass run hooks explicitly 2026-06-21 15:59:28 +08:00
Xubin Ren b6a9a9728a test(sdk): cover concurrent run hook isolation 2026-06-21 15:59:28 +08:00
michaelxerandXubin Ren a19725bc57 docs: add note about ephemeral guard and per-call hooks contextvar
Document that SDK callers always go through run() → process_direct(ephemeral=False),
so per_call hooks are intentionally excluded from ephemeral turns per chengyongru
review feedback.
2026-06-21 15:59:28 +08:00
michaelxerandXubin Ren 345ef80571 fix: update facade tests for contextvar hooks + fix ephemeral guard
The test_run_populates_tools_used_across_iterations,
test_run_populates_final_messages, and
test_run_user_hooks_still_fire_alongside_capture tests were reading
bot._loop._extra_hooks directly in their fake_process_direct mocks,
but the contextvar change moved per-call hooks to _per_call_hooks.

Also fixes the ephemeral guard: per_call hooks should not be used in
ephemeral mode (the original code incorrectly applied them regardless).
2026-06-21 15:59:28 +08:00
michaelxerandXubin Ren 0bb1b0b3c2 fix(sdk): use contextvars for per-call hooks to prevent concurrent run() race
Nanobot.run() previously mutated the shared self._loop._extra_hooks
attribute under a try/finally. When two run() calls with different
session_keys execute concurrently, they overwrite each other's hook
lists — the second call saves the first call's hooks as 'prev', and
the finally block restores stale state.

Use a contextvars.ContextVar instead, which is per-task in asyncio.
This gives each concurrent run() call its own hook list without
changing any function signatures. Falls back to self._extra_hooks
when no per-call hooks are set (non-SDK usage).
2026-06-21 15:59:28 +08:00
Xubin Ren fede2ecfc2 test(websocket): expect optional Keenable search key 2026-06-21 15:22:13 +08:00
Xubin Ren d30f3d466d fix(webui): allow optional Keenable search key 2026-06-21 15:22:13 +08:00
5feb6f485f refactor(web): use module constant for Keenable search URL
Match the _BOCHA_SEARCH_API_URL / _VOLCENGINE_SEARCH_API_URL convention
instead of hardcoding the URL inline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:22:13 +08:00
74daa81a1b feat(web): allow Keenable search without an API key
Keenable's public endpoint serves the free tier (1000 req/hour) without
auth. Route to /v1/search/public with the X-Keenable-Title header when no
key is configured, instead of falling back to DuckDuckGo; keep the
authenticated /v1/search path when an apiKey or KEENABLE_API_KEY is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:22:13 +08:00
yu-xin-candXubin Ren a5768a4ebe fix(tools): reject unknown builtin parameters 2026-06-21 15:22:09 +08:00
Xubin Ren 85036bacb6 test(telegram): cover rich message fallback latch 2026-06-21 15:22:05 +08:00
NanoBotandXubin Ren a8c65b50ca fix: narrow rich capability error detection to prevent false latch
- Remove overly broad 'not found' match that could trigger on transient
  errors like 'chat not found' or 'message to reply not found'
- Keep only 'method not found' and 'unknown method' which indicate
  the server genuinely doesn't support sendRichMessage
- Demote timeout log from warning to debug since it falls back to
  legacy path successfully
2026-06-21 15:22:05 +08:00
NanoBotandXubin Ren e9494c1dce feat(telegram): add Bot API 10.1 sendRichMessage support
- Try sendRichMessage for all non-blockquote messages (tables, task lists,
  math, collapsible, etc.) with graceful fallback to legacy HTML
- Fix payload format to use top-level 'markdown' field per Bot API 10.1 spec
- Add rich path in send_delta() stream end (delete preview + send rich)
- Latch off permanently if server returns capability error
- Removes _needs_rich_rendering() gate and 4 regex patterns
2026-06-21 15:22:05 +08:00
Xubin Ren 98916a31e3 perf(tokens): document tool schema cache assumption 2026-06-21 13:06:34 +08:00
yu-xin-candXubin Ren 7275a81ec7 perf(tokens): cache tool schema estimates 2026-06-21 13:06:34 +08:00
Xubin Ren a81c5e704a fix(memory): reject negative history cursors 2026-06-21 13:05:57 +08:00
Stellar鱼andXubin Ren be058c0922 fix(memory): keep history cursor monotonic 2026-06-21 13:05:57 +08:00
franciscomaestreandXubin Ren 99e158c062 feat(whatsapp): seed LID->phone mappings on startup
WhatsApp can deliver a sender LID instead of a phone number. The channel
already learns the LID->phone mapping at runtime, but only after a message
that carries both values, so the first message from a contact can't be
resolved to a phone number.

Seed the mapping on startup from two sources:

- reverse mapping files the bridge persists in the auth directory
  (lid-mapping-<lid>_reverse.json), resolved via get_runtime_subdir so it
  respects a custom runtime dir
- a new optional channels.whatsapp.lidMappings config dict for static
  mappings (takes precedence over the on-disk files)

Malformed/empty mapping files are ignored rather than failing startup.
Adds tests for both sources, precedence, malformed files and the
no-auth-dir case, plus docs for the new config field.
2026-06-21 13:05:06 +08:00
Xubin Ren e81968a32b test: isolate MCP timeout regression from DNS 2026-06-20 19:12:36 +08:00
Xubin Ren 0eae7ceb1a test: use safe URL for MCP timeout regression 2026-06-19 21:07:51 +08:00
Xubin Ren bc8f42c639 test(mcp): cover streamable HTTP timeout 2026-06-19 21:07:51 +08:00
Luc NguyenandXubin Ren d7abf39169 fix: set httpx timeout for streamableHttp transport to prevent event loop blocking
timeout=None causes the httpx client to wait indefinitely when connecting
to MCP servers via streamableHttp transport, blocking the entire event loop.
This prevents Telegram polling and other async operations from running.

Set timeout=httpx.Timeout(30.0, connect=10.0) to match the pattern used
in the SSE transport's httpx_client_factory.
2026-06-19 21:07:51 +08:00
yorkhellenandXubin Ren 33638417be fix(session): delete_session also removes legacy path files to prevent history revival
SessionManager._load() migrates sessions from the legacy directory
(~/.nanobot/sessions/) to the workspace path, but delete_session only
checked the workspace path. A user deleting a session could therefore
see its history come back the next time the session was loaded.

- delete_session now attempts to unlink both paths
- returns True if at least one file was removed
- added regression tests: legacy-only, both-paths, and no-revival
2026-06-19 21:07:47 +08:00
sbyininandXubin Ren 6da56f3574 Fix OpenAI reference image home expansion test on Windows 2026-06-19 17:16:09 +08:00
sbyininandXubin Ren 44f7bbae50 fix openai image reference home paths 2026-06-19 17:16:09 +08:00
sbyininandXubin Ren adb737b614 fix openai image reference edits 2026-06-19 17:16:09 +08:00
chengyongruandXubin Ren dd4d410ce2 refactor(feishu): simplify table extraction
Maintainer edit: remove speculative recursive cell handling and keep table extraction to the documented columns and row values.
2026-06-19 17:16:04 +08:00
chengyongruandXubin Ren 215379ac63 fix(feishu): extract table card rows
Maintainer edit: parse Feishu card table columns and rows so forwarded table cards do not fall back to [interactive], and add parser regression coverage.
2026-06-19 17:16:04 +08:00
Jiajun XieandXubin Ren bc59fe8719 fix(feishu): support reading WebSocket rendered card content
Feishu cards arriving via WebSocket have a different structure than
expected: elements are nested lists with tag:text/text fields instead
of flat lists with tag:markdown/content. Also extract user_dsl for
richer card data and support body.elements (schema 2.0).
2026-06-19 17:16:04 +08:00
Xubin Ren 58a14d18dd test: tighten image placeholder assertions 2026-06-19 14:59:52 +08:00
Xubin Ren de5e216583 test: fix image strip test formatting 2026-06-19 14:59:52 +08:00
michaelxerandXubin Ren 7917466f4e fix(tests): update image placeholder assertions for new non-descriptive text
The _strip_image_content methods now use a fixed non-descriptive
placeholder instead of path-derived text. Update the 3 existing
test_provider_retry assertions to match the new placeholder format.
2026-06-19 14:59:52 +08:00
michaelxerandXubin Ren 9ed3905a23 fix(providers): use non-descriptive placeholder when stripping images
The image-strip fallback (triggered when a model errors on image input)
replaced image_url blocks with [image: <path>] or [image omitted]. Both
read like a live, available image to the LLM, causing it to:

1. hallucinate about image contents it never received
2. attempt read_file on the leaked server path
3. expose internal file paths to the model

Replace with an explicit '[Image not delivered to model — do not describe
or reference it]' placeholder that tells the LLM the image was stripped.

Fixes #4345
2026-06-19 14:59:52 +08:00