3771 Commits

Author SHA1 Message Date
NearlCrews
5eb818e800 fix(providers): require api_base before local provider wins on keyword match
Ollama's spec keeps "nemotron" as a keyword so bare `nemotron-3-nano`
auto-routes to a configured Ollama install (PR #1863). NVIDIA NIM was
later registered with the same "nemotron" keyword (commit 046d0831),
creating the only keyword collision in the registry.

In `_match_provider`, the keyword loop accepted any local provider on
`spec.is_local` alone — no api_base check. Models like
`nvidia/nemotron-3-super-120b-a12b` (intended for OpenRouter or NVIDIA
NIM) were therefore hijacked to http://localhost:11434/v1 even when the
user had never configured Ollama, causing silent connection errors at
runtime.

Add the same api_base gate the local-fallback loop already uses: a local
provider only wins by keyword when the user has actually set its
api_base. Preserves PR #1863's intent for users who configured Ollama;
fixes the silent hijack for everyone else.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-08-01 20:25:58 +08:00
santhreal
4c387f6633 fix(memory): handle non-string timestamp and missing role in raw_archive 2026-08-01 20:14:28 +08:00
Xubin Ren
e152e7bc0b test(cron): cover stop during manual execution 2026-08-01 20:03:19 +08:00
yu-xin-c
e26e09c205 fix(cron): preserve manual run completion state 2026-08-01 20:03:19 +08:00
KDB
f3bbb543d0 refactor(cli): narrow Pyright suppressions 2026-08-01 19:52:08 +08:00
KDB
b1030ab131 fix(exec): preserve wait targets across response truncation 2026-08-01 19:40:36 +08:00
KDB
39bb20c76b fix(session): tolerate malformed persisted session summary
AutoCompact.prepare_session runs on the turn hot path
(AgentLoop._compact_session) and read the persisted _last_summary metadata
with an unguarded meta['text'] and datetime.fromisoformat(meta['last_active']).
A _last_summary dict that was hand-edited or written by another version
(missing text/last_active, or a non-ISO last_active) raised KeyError/ValueError
out of the turn.

Sibling readers already tolerate the same data: estimate_session_prompt_tokens
uses .get('text') and _archive parses inside try/except. Mirror that tolerance:
skip when text is unusable, and fall back to the session's own updated_at (the
value the writer persists) when last_active is missing or unparseable, so the
archived summary is preserved instead of crashing the turn.
2026-08-01 19:29:16 +08:00
chengyongru
cdb75f8e7d
feat(providers): support DeepSeek Responses API (#5197) 2026-08-01 11:53:51 +08:00
chengyongru
971b977a84
fix(weixin): recover refreshed state after session expiry (#5196) 2026-08-01 00:28:21 +08:00
Pablo Cabeza García
54650332fb fix(slack): scope channel thread openers to their own session
A top-level channel message that opens a thread fell back to the
channel-wide session, because the session key required `raw_thread_ts` —
which Slack only sets on messages that already arrived inside a thread.
Every new thread therefore began life in one shared channel session and
only became thread-scoped from its first reply onward, so unrelated
threads saw each other's opening turns.

Key off `thread_ts` instead. It is set both for messages arriving inside
a thread and for channel messages that `reply_in_thread` opens a thread
for. DM roots never get a `thread_ts`, so they keep the default per-chat
session and the DM routing from 82c5083 is preserved; with
`reply_in_thread` disabled no thread exists and the channel session is
still used.

This restores the per-thread isolation introduced in #1048.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:47:26 +08:00
chengyongru
172fe4f991
fix(webui): preserve user scroll ownership near tail (#5193) 2026-07-31 23:37:26 +08:00
shixi-li
dda9b61b1e fix(config): install timezone data on all platforms 2026-07-31 19:55:22 +08:00
chengyongru
6a1a45d07a
feat: preserve Responses reasoning state and compact context (#5172) 2026-07-30 22:39:43 +08:00
Solaris-star
511c764f45 fix(agent): route finish_reason='length' with blank content to length recovery
When an LLM response arrives with finish_reason='length' and has_tool_calls
but blank text content (e.g. the model spent its whole output budget on a
tool call whose closing tag was truncated), the runner dropped the tool
calls and then misrouted the blank response into the empty-response retry
branch. Retrying the same prompt cannot recover from output-budget
exhaustion, so every retry hit the same length ceiling and the turn ended
in the generic apology.

The length-recovery branch was gated on 'finish_reason == length and not
is_blank_text(clean)', so a blank-but-truncated turn could never reach it.

- The empty-response retry branch now excludes finish_reason == 'length'
  (in addition to 'error').
- The length-recovery branch no longer requires non-blank content, so a
  blank-but-truncated turn enters recovery and appends
  build_length_recovery_message (which handles a blank tail safely).

Adds a regression test asserting the length-recovery path is taken; it
fails on the unfixed code and passes with the fix.

Fixes #5133
2026-07-30 19:55:19 +08:00
Xubin Ren
0eac82984c test(mcp): stabilize idle reconnect timing 2026-07-30 19:44:09 +08:00
yu-xin-c
5e67fbf93e fix(exec): bound buffered session output 2026-07-30 19:44:09 +08:00
yu-xin-c
9ec4420104 fix(agent): release idle session locks 2026-07-30 19:17:37 +08:00
KDB
52680dbe19 fix(pairing): keep approvals across transient store read failures
_load() treated any OSError like corruption and returned an empty store. When pairing.json was transiently unreadable, an unapproved DM could deny the sender, generate a pairing code from the empty view, and overwrite the store without its approved senders.

Keep the existing JSONDecodeError reset behavior, but propagate OSError so mutations cannot persist unreadable state. Read-only checks fail closed without writing; mutating /pairing subcommands report temporary unavailability; and the DM pairing path skips one reply instead of crashing the handler.

This mirrors the refuse-to-overwrite strategy used by the cron and trigger stores.
2026-07-30 19:02:37 +08:00
KDB
e633f867e8 fix(session): tolerate invalid idle-compaction timestamps 2026-07-30 18:52:16 +08:00
KDB
07c2677eed fix(webui): drop malformed token-usage day keys
normalize_token_usage_state only length-checked persisted day keys, so a
hand-edited or foreign 10-char key (e.g. "not-a-dat3" or "2026-13-01") in
token-usage.json survived reads and atomic rewrites. token_usage_payload
then parsed every day key with an unguarded datetime.fromisoformat, so one
such key failed every /api/settings and /api/settings/usage request until
the file was repaired by hand.

Validate day keys in normalize_token_usage_state, the shared boundary that
every read, record, and rewrite already funnels through. Malformed keys are
dropped like other malformed rows and scrubbed from the file on the next
write; valid state is unchanged.
2026-07-30 18:41:44 +08:00
ATECHPCS
92361cbeac fix(gitstore): return real git object ids instead of hex-of-hex
`porcelain.commit()` and `repo.refs[...]` hand back object ids as a
40-character hex string that is already encoded to bytes. Calling `.hex()`
on that encodes the ASCII a second time, so every id GitStore produced or
displayed was double-encoded:

    auto_commit()          -> '62623234'
    git log --abbrev=8     -> 'bb244606'

The module is self-consistently wrong, so `/dream-log` and `/dream-restore`
work as long as the id came from nanobot itself. What does not work is
crossing the boundary: ids in logs and commit output match nothing in
`git log`, and an id copied from `git log` cannot be resolved:

    _resolve_sha(own id)      -> b'bb244606d780...'
    _resolve_sha(real git id) -> None

Use `.decode()` at the four sites that consume dulwich object ids. Nothing
persists an id — callers either display it or resolve it live — so there is
no stored state in the old format.

Adds two regression tests: the id returned by `auto_commit` must equal
`git log --abbrev=8`, and a real git id must resolve through `_resolve_sha`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-30 18:25:56 +08:00
chengyongru
bb2f6cf324 fix(webui): preserve automation source on streamed replies 2026-07-30 17:57:31 +08:00
chengyongru
606ac56e8f
feat(webui): support remote Codex OAuth login (#5174) 2026-07-30 15:06:34 +08:00
chengyongru
e2563e2e74
refactor(cli): split commands into focused modules (#5175) 2026-07-30 15:01:35 +08:00
chengyongru
ad6900e56c
refactor(session): separate persistence behind SessionStore (#5170) 2026-07-30 11:51:13 +08:00
chengyongru
c33c188afb
fix(session): preserve history during idle compaction (#5167) 2026-07-30 10:45:45 +08:00
chengyongru
11fcd9cc5f
fix(webui): prevent redundant thread and media reloads (#5164) 2026-07-30 10:25:22 +08:00
chengyongru
fc73d5ff39 fix(webui): avoid false microphone silence errors 2026-07-30 09:02:40 +08:00
Xubin Ren
0fe3c5aa2c fix(webui): satisfy strict skill marketplace typing 2026-07-30 01:13:37 +08:00
Xubin Ren
c440695aef fix(webui): harden skill marketplace lifecycle 2026-07-30 01:13:37 +08:00
Xubin Ren
8a56eb06ad fix(webui): harden skill management 2026-07-30 01:13:37 +08:00
Xubin Ren
d276fe1386 perf(webui): make marketplace filters instant 2026-07-30 01:13:37 +08:00
Xubin Ren
12ebc7b8e2 style(webui): use package install icon 2026-07-30 01:13:37 +08:00
Xubin Ren
164726c43f style(webui): quiet marketplace install actions 2026-07-30 01:13:37 +08:00
Xubin Ren
62b132b698 style(webui): reduce repeated marketplace labels 2026-07-30 01:13:37 +08:00
Xubin Ren
e66eb204d0 feat(webui): add SkillHub marketplace source 2026-07-30 01:13:37 +08:00
Xubin Ren
42ee34e34d fix(webui): localize skills view tabs 2026-07-30 01:13:37 +08:00
Xubin Ren
73d3a49a27 style(webui): float in conversation highlight 2026-07-30 01:13:37 +08:00
Xubin Ren
0a8bc0ac29 style(webui): slide active conversation highlight 2026-07-30 01:13:37 +08:00
Xubin Ren
962cdb968d style(webui): animate settings section transitions 2026-07-30 01:13:37 +08:00
Xubin Ren
7561d846fa style(webui): highlight selected conversations 2026-07-30 01:13:37 +08:00
Xubin Ren
cad9b07deb fix(webui): render adjacent CJK emphasis 2026-07-30 01:13:37 +08:00
Xubin Ren
9c6e1b073f fix(webui): align ordered list markers 2026-07-30 01:13:37 +08:00
Xubin Ren
9b5a5c5302 style(webui): refine skills mobile experience 2026-07-30 01:13:37 +08:00
Xubin Ren
655cd80651 style(webui): clarify skill row interactions 2026-07-30 01:13:37 +08:00
Xubin Ren
9ceacc540c fix(webui): avoid parsing currency as math 2026-07-30 01:13:37 +08:00
Xubin Ren
ba0ba4749d feat(webui): add skills marketplace 2026-07-30 01:13:37 +08:00
chengyongru
129b74b4cf
feat(webui): track optimistic message delivery status (#5162) 2026-07-29 23:15:45 +08:00
Zhou
5a28a6165c
fix(shell): preserve UTF-8 native input on PowerShell 5 (#5160)
Windows PowerShell 5.1 defaults $OutputEncoding to US-ASCII, corrupting non-ASCII strings piped to native commands. Set it from the console encoding only on legacy versions so PowerShell 7 keeps its defaults.
2026-07-29 21:47:01 +08:00
chengyongru
757ad9c764
refactor: enforce BasedPyright strict type checking (#5158) 2026-07-29 21:37:11 +08:00