Commit Graph
368 Commits
Author SHA1 Message Date
chengyongruandXubin Ren 0ae28571e7 fix(agent): close file edit activity on cancellation 2026-07-09 10:42:43 +08:00
chengyongruandXubin Ren 7768672c5b feat: add file edit diff progress view
Capture file edit snapshots through runner tool lifecycle hooks and render unified diffs in the WebUI with folding and truncation controls.
2026-07-09 10:42:43 +08:00
chengyongruandXubin Ren 8559458258 refactor(agent): add turn hook factories 2026-07-08 21:02:12 +08:00
chengyongruandXubin Ren 04dbf17426 test(agent): cover turn hook ordering 2026-07-08 21:02:12 +08:00
chengyongruandXubin Ren a52a88455b refactor(agent): extract turn hook assembly 2026-07-08 21:02:12 +08:00
chengyongruandXubin Ren 3f33ff3143 chore: remove unused dead code 2026-07-07 15:41:27 +08:00
hamb1yandXubin Ren 9eade9be5f test: update quick start provider expectations 2026-07-06 12:13:00 +08:00
Kenneth ZhaoandGitHub f0c989ba2d fix(dream): ground memory audit records in the real git diff (#4673)
* fix(dream): ground commit messages and cursor advance in the real git diff

Dream consolidation could emit a /dream-log audit record that did not match
the actual file changes: build_dream_commit_message appended the LLM's
unverified resp.content, dream_run_completed only checked the stop reason, and
file contents were deliberately omitted from the prompt. The combination let a
single-turn self-report become the durable audit record.

- gitstore: add summarize_working_tree() — a structured, machine-derived
  summary (per-file +N/-M, totals, capped unified diff) of working-tree
  changes vs HEAD. Pure filesystem/git ground truth, never LLM narrative.
- memory: build_dream_commit_message now takes the diff body instead of resp;
  dream_content_diff() exposes the real delta over SOUL/USER/MEMORY.md only
  (excludes .dream_cursor so cursor writes aren't mistaken for edits);
  build_dream_prompt embeds current file contents so the model edits reality,
  not a stale mental model.
- builtin/cli: both Dream paths now compute the diff, gate cursor advance on
  a non-empty delta (no-op runs no longer swallow history), and commit with
  the diff-grounded message. Non-git workspaces fall back to the completion
  check.
- dream.md: document that contents are embedded, and add a chain-of-
  verification guardrail so the model's summary cannot claim unmade edits.

A regression test proves a lying resp.content never reaches the audit log
while the real diff does.

* fix(dream): mark non-UTF-8 memory files as binary in diff summary

Address review feedback (Q1 on PR #4673): summarize_working_tree read
working-tree files with errors="replace", which would emit U+FFFD
replacement chars into the audit record if a memory file ever held
invalid UTF-8 — misrepresenting the diff it is meant to make truthful.

Switch to errors="strict" and catch UnicodeDecodeError: a non-UTF-8
(or binary/corrupt) file is now recorded as "{path}: binary or
non-UTF-8 file changed" and omitted from the unified diff, so the
audit record stays honest. An empty diff block is also suppressed when
all changes are binary.

Adds a defensive regression test asserting no replacement char leaks.
2026-07-06 12:12:55 +08:00
chengyongruandXubin Ren 614ea86a81 test: align MCP transient reconnect coverage
maintainer edit: update the existing transient retry tests for reconnect-first behavior and keep structured retry-failure coverage in the focused MCP transient suite.
2026-07-04 21:15:18 +08:00
LILACandGitHub b19a744110 fix(providers): update Anthropic default model to claude-sonnet-4-6
Update the Anthropic provider default model and matching docs/tests from claude-sonnet-4-20250514 to claude-sonnet-4-6.

Fixes #4675
2026-07-03 15:46:19 +08:00
Xubin Ren c9c69e4316 fix(memory): cap workspace Dream prompt overrides 2026-07-03 00:41:51 +08:00
chengyongruandXubin Ren f38fd7d5d3 feat(memory): add workspace Dream prompt override 2026-07-03 00:41:51 +08:00
chengyongruandXubin Ren 5af22042ec fix(trigger): keep local worker alive on turn cancel 2026-07-03 00:36:29 +08:00
chengyongruandXubin Ren aecb5fbc33 fix(agent): avoid tool compaction echo loops 2026-07-03 00:36:22 +08:00
chengyongruandXubin Ren 34535b4e7c fix(webui): hide subagent backfill payloads 2026-07-02 14:36:23 +08:00
Stellar鱼andXubin Ren 5abe06f808 test: cover runner blocked tool-call finish reasons 2026-07-02 14:36:18 +08:00
chengyongruandXubin Ren acb0e853ff refactor(trigger): share automation turn delivery 2026-07-02 13:32:46 +08:00
chengyongruandXubin Ren f32007c83f fix(trigger): defer local triggers until session idle 2026-07-02 13:32:46 +08:00
chengyongruandXubin Ren 2ebf5c4972 refactor(trigger): name CLI trigger source as local
maintainer edit: cron is also a trigger source, so keep the new CLI-delivered source explicitly named as local trigger across backend, WebUI, docs, and tests.
2026-07-02 13:32:46 +08:00
chengyongruandXubin Ren 55b550ee01 fix(trigger): hide external trigger inputs 2026-07-02 13:32:46 +08:00
chengyongruandXubin Ren 5f4cfbcb16 refactor(bus): type outbound runtime events 2026-07-01 20:17:00 +08:00
chengyongruandXubin Ren ed48325346 fix: cover API auth guard regressions
Maintainer edit: restore CI by updating serve/onboard tests, add auth/config coverage, and keep auth failures on the OpenAI-compatible error shape.
2026-07-01 13:09:49 +08:00
chengyongruandXubin Ren b0258e8b20 fix: preserve legacy plugin tool errors 2026-07-01 13:03:47 +08:00
chengyongruandXubin Ren 8493560976 refactor(tools): use structured tool error results 2026-07-01 13:03:47 +08:00
chengyongruandXubin Ren 840ba5af33 fix: simplify session recency activity tracking
maintainer edit: remove the _last_compacted_at maintenance state, gate idle compaction on whether a session still has a removable tail, and sort WebUI sessions by the latest visible transcript activity.
2026-06-30 23:38:32 +08:00
chengyongruandXubin Ren 3403b87641 fix(webui): keep idle compaction out of session recency 2026-06-30 23:38:32 +08:00
axelray-devandXubin Ren 5692f7a68a refactor(session): return RetentionResult instead of bare tuple
Replace the tuple(list[dict], int) return of
Session.retain_recent_legal_suffix with a named RetentionResult
dataclass that exposes retained, dropped,
already_consolidated_count, and new_last_consolidated fields.

The tuple return was easy to misuse because the second value only
made sense relative to the first and the old last_consolidated
cursor. The named fields make the archive-skip semantics explicit
at every call site.

No behavior change. All existing tests pass unchanged in semantics.

Refs #4136

Signed-off-by: axelray-dev <110029405+axelray-dev@users.noreply.github.com>
2026-06-29 14:24:00 +08:00
chengyongruandXubin Ren 57f0c859fc refactor(context): trim replay cap plumbing 2026-06-29 14:23:55 +08:00
chengyongruandXubin Ren 40282e3b74 fix(context): scale replay cap with context window 2026-06-29 14:23:55 +08:00
chengyongruandXubin Ren dacc699293 fix(config): retire max messages setting 2026-06-29 14:23:55 +08:00
chengyongruandXubin Ren c8638dee46 fix(context): raise max messages fallback cap
Treat max_messages as a last-resort replay guard now that consolidation and idle auto-compact own normal history reduction. Raising the default avoids frequent sliding-window prefix churn in moderate conversations without adding a new cache-policy knob.
2026-06-29 14:23:55 +08:00
Xubin Ren 7dc45ff94e chore: tighten malformed tool-call guard wording 2026-06-28 19:46:59 +08:00
8248d075db fix(agent): harden tool-call handling against malformed upstream relays
Combine malformed tool-call handling with placeholder filtering and a
no-tools fallback so a relay that returns tool_use blocks with null
id/name/input can no longer crash a turn or permanently wedge a session.

Adapted to the ContextGovernor architecture (context governance now lives
in nanobot/agent/context_governance.py, not runner.py):

- ToolCallRequest.has_valid_name(): single source of truth for "usable
  name" (non-empty string).
- tool_hints.format_tool_hints(): skip tool calls with a non-string/empty
  name instead of raising AttributeError on the whole turn.
- ContextGovernor.strip_placeholder_assistant_messages() and
  strip_malformed_tool_calls() (plus the _tool_call_name_is_valid helper):
  history-cleaning staticmethods invoked at the START of
  prepare_for_model() — strip_placeholder, then strip_malformed, then the
  existing drop_orphan/backfill chain. Both only repair the model-facing
  copy and leave persisted history untouched (return a copy, or the same
  list when nothing changes). Also wired into runner's minimal-repair path.
- AgentRunner._drop_malformed_tool_calls(): returns
  (dropped, all_dropped, original_finish_reason); clears finish_reason to
  "stop" when all calls are dropped.
- AgentRunner._malformed_tool_call_retry_messages() + _request_model
  malformed_retry flag: when an all-dropped tool_calls response comes back,
  retry once with a corrective note; if the retry STILL comes back
  all-dropped, fall back to _request_no_tools for graceful text degradation.

Tests for the history-cleaning methods live with ContextGovernor in
tests/agent/test_runner_governance.py; response-layer and tool-hint tests
stay on AgentRunner / tool_hints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:46:59 +08:00
Xubin Ren c90e433057 fix(session): guard lossy migration by stored key 2026-06-27 16:52:54 +08:00
axelray-devandXubin Ren 00a907c493 fix(session): split safe_key and _storage_key to fix WebUI coupling (#4533) 2026-06-27 16:52:54 +08:00
axelray-devandXubin Ren cf2f589615 fix(session): prevent save from writing to legacy lossy path, add collision tests (#4533) 2026-06-27 16:52:54 +08:00
Xubin Ren 6a27c26257 test: cover non-stream duplicate tool call ids 2026-06-27 16:47:48 +08:00
r4sk1nandXubin Ren 47dcc61e9b test(agent): fix flaky test_keeps_n_most_recent by ensuring sequential mtimes 2026-06-27 16:29:31 +08:00
chengyongruandXubin Ren 3460ca3cb9 fix(agent): gate microcompaction on context pressure
Extract model-facing context governance from AgentRunner.

Only compact in-flight tool results when the model request is over budget, keep compacted IDs stable within a turn, and allow the newest result to be compacted as a last resort when it is the remaining source of overflow.
2026-06-27 11:04:41 +08:00
chengyongruandXubin Ren 9b45fc1172 fix(session): remove message time replay prefixes 2026-06-27 11:04:36 +08:00
axelray-devandXubin Ren 851a0ff50c fix: make subagent fail_on_tool_error configurable (#4198)
Add fail_on_tool_error to AgentDefaults and wire it through
AgentLoop -> SubagentManager -> AgentRunSpec.

Previously hardcoded to True in SubagentManager._run_subagent.
Now configurable via config.json with default True for backward
compatibility. When set to False, subagents can retry on minor
tool errors instead of immediately failing.

Changes:
- nanobot/config/schema.py: add fail_on_tool_error field (default True)
- nanobot/agent/subagent.py: accept and forward fail_on_tool_error
- nanobot/agent/loop.py: pass config through to SubagentManager
- tests/agent/test_subagent.py: add regression test

Signed-off-by: axelray-dev <110029405+axelray-dev@users.noreply.github.com>
2026-06-25 22:53:36 +08:00
axelray-devandXubin Ren 6c880a6691 fix: advance dream cursor when Dream is disabled to prevent prompt bloat (#4242)
When dream.enabled is false, the Dream cron job never runs, so the
dream cursor (.dream_cursor) stays at its initial value (0). This
causes read_recent_history_for_prompt() to treat every history entry
as unprocessed, injecting the full chat history into every system
prompt and growing without bound.

Fix: fast-forward the dream cursor to the latest history entry at
gateway startup when Dream is disabled.
2026-06-25 22:53:10 +08:00
9354b80a6e fix(cli): show search engines (incl. Keenable) in onboard wizard
The onboard wizard dispatched field handlers by bare field name, so
WebSearchConfig.provider was hijacked by the LLM-provider handler and
showed LLM providers instead of search engines. Keenable was also never
wired into the CLI wizard when it landed in the WebUI.

- Add a single source of truth for selectable search providers
  (SEARCH_PROVIDER_OPTIONS in web.py); WebUI settings now import it.
- Add a WebSearchConfig-aware search-provider picker to the wizard and
  resolve handlers by (model type, field name) so the LLM and search
  provider fields no longer collide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:52:50 +08:00
chengyongruandXubin Ren 98dd883ce8 fix: buffer split reasoning wrapper deltas
maintainer edit: native reasoning streams can split <thinking> wrapper tags across chunks. Buffer the stream and emit only cleaned incremental reasoning so raw partial tags do not reach WebUI.
2026-06-24 15:45:34 +08:00
ZhouandXubin Ren 596bf5398c test: cover empty thinking marker streaming 2026-06-24 15:45:34 +08:00
ZhouandXubin Ren 523bb928bf fix: normalize thinking tags in reasoning output 2026-06-24 15:45:34 +08:00
chengyongruandXubin Ren 0e9861558a fix: keep duplicate id repair in Anthropic provider
maintainer edit: move duplicate tool_use history repair out of AgentRunner and into Anthropic message conversion, reusing the OpenAI-compatible queue-mapping approach locally without broadening the shared runner path.
2026-06-24 10:21:48 +08:00
chengyongruandXubin Ren 853aecdb97 fix: preserve duplicate-id tool calls
maintainer edit: remap duplicate tool_use/tool_call ids instead of dropping later calls, so Anthropic-compatible providers that reuse ids for distinct parallel tool calls keep all requested work while still sending unique ids.
2026-06-24 10:21:48 +08:00
Teddy YanandXubin Ren 6b8e832ba5 fix: address PR review comments - rename _dedup to _dedupe and fix ID storage consistency 2026-06-24 10:21:48 +08:00
6689e2d377 fix(providers): dedupe tool_use ids to prevent Anthropic 400s
Anthropic rejects any request where two tool_use blocks share an id
("messages.N.content.M: tool_use ids must be unique"). A mis-assembled
stream could surface the same tool_use block twice in one assistant turn;
the runner persisted it verbatim, so the malformed message was re-sent on
every subsequent turn and permanently bricked the session — the agent
silently stopped replying.

Fix at two layers:
- AnthropicProvider._parse_response: drop duplicate tool_use ids (keep
  first) as the response enters nanobot, so corruption is never persisted.
- AgentRunner._dedup_tool_calls: a new context-governance pass that dedupes
  assistant tool_calls and tool results by id before each send, healing any
  history that was already corrupted.

Add regression tests covering both the dedup and the no-op fast path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 10:21:48 +08:00