- Change enable_memory_tool default from False to True
- Update tests to reflect new default behavior
- Since this is a personal instance with no other users, opt-out
makes more sense than opt-in for a requested feature
- Add enable_memory_tool parameter to AgentLoop.__init__
- Memory tool only registered when flag is True
- 2 new integration tests in test_memory_integration.py, all passing
- Add enable_memory_tool field to ToolsConfig (default: False)
- Supports both snake_case and camelCase variants
- 3 new tests in test_memory_config.py, all passing
- Insert: add text at specific line number with validation
- Delete: remove files or directories recursively
- Rename: move/rename with collision detection
- All commands follow path security and CLIResult pattern
- 6 new tests in test_memory_commands.py, all passing
- Total 34 memory tests passing
- Switch from sentence-boundary splitting to upstream's simpler approach
- Uses max_len=4000 (safer buffer vs 4096 limit)
- Split priority: line breaks → spaces → hard cut
- Battle-tested implementation from HKUDS/nanobot upstream
- Simpler, more maintainable code
- Works better for both prose and code/logs
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add _send_text_chunks method to split messages at sentence boundaries
- Messages exceeding Telegram's 4096 character limit now send as multiple messages
- Per design doc: docs/plans/2026-02-27-openclaw-telegram-features-design.md
- All new tests pass (4/4), no regressions
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete implementation of all three native Anthropic tools:
- bash_20250124: Shell command execution
- text_editor_20250124: File editing operations
- computer_20251124: VNC desktop control (all 17 actions)
Includes provider updates, test improvements, and registry changes.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Registry now supports executing both native Anthropic tools (via __call__)
and function tools (via execute). Native tools return ToolResult/CLIResult
objects instead of strings.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add test confirming BashTool20250124, EditTool20250728, and
ComputerTool20251124 are registered in AgentLoop on initialization.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Verify that screenshots from computer tool are tracked in
media_paths_for_turn and included in OutboundMessage.media.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Apply same result type handling logic to _process_system_message
to support native tools in subagent/system contexts.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaced by BashTool20250124 and EditTool20250728 which provide
better functionality via model-trained behaviors.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
BashTool20250124, EditTool20250728, and ComputerTool20251124 are now
automatically registered alongside existing function tools.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Screenshots from computer tool are saved to disk and included in
OutboundMessage.media for channel delivery to users.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Comprehensive tests for:
- ToolResult with output field
- ToolResult with error field
- ToolResult with base64_image field
- CLIResult handling
- Legacy string results (backward compatibility)
- Combined output and error fields
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Agent loop now processes ToolResult and CLIResult from native tools,
while maintaining backward compatibility with string results.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
CLIResult now requires exit_code, output, and error fields after
EditTool implementation. Update test to match new signature.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add computer_20251124 for VNC desktop control. Supports keyboard,
mouse, and screenshots via vncdotool.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add test_edit_tool_requires_absolute_path and test_edit_tool_to_params
to complete test coverage per specification.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Make test more specific by expecting exact sorted order
instead of checking both permutations.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extract beta_flag from tool objects before conversion and add
to API request headers. Supports multiple flags via comma-join.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
_convert_tools_to_anthropic now passes through native tool format
(bash_20250124, etc.) while still converting function tools.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Registry now supports both function tools (to_schema) and native
tools (to_params) via hasattr checks. Enables mixed tool types.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add BaseAnthropicTool, ToolResult, CLIResult, and ToolError.
These support native tools with version-coupled behaviors.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Design for integrating bash_20250124, text_editor_20250728, and
computer_20251124 native tools into nanobot. These tools leverage
model-trained behaviors instead of instruction-following.
Key approach: Duck-typed registry supporting both function tools
and native tools, with beta flag management and ToolResult handling.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Root cause: send_document and send_audio were receiving raw bytes
without filename metadata, causing Telegram to use generic
"application.octet-stream" name.
Solution: Extract filename from path and pass via filename parameter
to send_document/send_audio and their InputMedia counterparts.
Images/videos unaffected as Telegram infers names from content type.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The _send_with_media method uses Path but the import was missing from the top-level imports, causing "name 'Path' is not defined" error.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add missing media parameter to message tool, enabling agents to send media attachments via the message() tool.
Changes:
- message.py: Add media parameter to tool schema and execute() method
- telegram.py: Restore _send_with_media() method with album support
This completes the three-layer media architecture:
- Agent Loop: message tool now accepts media parameter
- Provider Layer: OutboundMessage carries media list
- Telegram Channel: _send_with_media processes and sends media
Fixes the root cause where agents had no way to specify media attachments, resulting in invented [file: ...] syntax in message content.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add context manager to PIL Image.open() to prevent file handle leaks
- Fix caption logic for separate media (track first non-album item)
- Improve exception handling with size checks and specific types
- Add constants for magic numbers (REMOTE_MEDIA_SIZE_LIMIT)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Processes local files and remote URLs
Optimizes images automatically
Groups 2+ images/videos into albums
Handles caption overflow (>1024 chars)
Routes to correct Telegram API methods
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Downloads from URLs with 10s timeout
Detects MIME from response headers or content
Enforces size limit
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Classifies MIME types into IMAGE/VIDEO/AUDIO/DOCUMENT
Used for routing to correct Telegram API methods
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Required for media handling:
- pillow-heif: HEIC to JPEG conversion
- python-magic: MIME type detection
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated descriptions to make it clear the message tool sends to the
main agent (who processes and decides how to respond), not directly
to the user.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Subagents can now send messages to users via the message tool.
Messages are routed through the main agent via the bus and preserve
metadata (e.g. suppress_output) from the originating message.
Changes:
1. Created SubagentMessageTool that creates InboundMessages
2. Publishes to bus with preserved metadata
3. Registered in subagent tool registry
4. Updated subagent system prompt to mention message capability
This allows subagents to communicate findings during execution while
respecting suppression flags (e.g. heartbeat subagents won't spam Telegram).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When a subagent completes and announces its result, it now inherits the
metadata (including suppress_output) from the original message that spawned it.
Changes:
1. SpawnTool.set_context() now accepts metadata parameter
2. SubagentManager.spawn() now accepts origin_metadata parameter
3. Origin dict now includes 'metadata' field
4. _announce_result() includes metadata when creating InboundMessage
5. All set_context() calls pass msg.metadata
This fixes the bug where heartbeat suppression was lost when subagents
announced their results - the subagent announcement would be visible on
Telegram even though the heartbeat itself was suppressed.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Store results in _task_results for ALL subagents, regardless of origin.
Then only announce to bus if it's a top-level subagent (not a child).
This fixes the bug where wait_for_subagents would return 'No result found'
for subagents spawned from the main telegram session, because their results
went to the bus instead of _task_results.
Now:
- All subagents store results in _task_results (so wait_for can find them)
- Child subagents (origin[channel] == 'subagent') return early (no announcement)
- Top-level subagents continue and announce via bus (so main agent gets notified)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The heartbeat service was incorrectly counting ALL messages with role="user"
as user activity, including system-generated messages (heartbeat prompts,
subagent announcements). This caused the idle detection to never trigger
because heartbeat's own messages were counted as user activity.
Changes:
1. Store sender_id in session messages (loop.py)
- Added sender_id=msg.sender_id to session.add_message() call
- Allows distinguishing real user messages from system-generated ones
2. Filter by sender_id in heartbeat idle detection (service.py)
- Real Telegram messages have sender_id like "239824268|username"
- System messages via process_direct have sender_id="user" (hardcoded)
- Heartbeat now skips messages with sender_id="user"
- Backwards compatible: messages without sender_id are treated as real
This is a robust, source-based solution that checks how messages are
CREATED rather than pattern-matching their content.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated existing tests to work with cryptographic visibility markers:
1. test_agent_loop_metadata.py:
- Updated test_suppress_mode_adds_hidden_prefix to verify [HIDDEN:signature] format
- Added validation for 8-character hex signature
- Updated test_normal_mode_no_hidden_prefix to check for [HIDDEN: prefix
2. test_idle_heartbeat_integration.py:
- Updated test_idle_heartbeat_end_to_end to search for [HIDDEN: prefix
- Added signature format validation (8-char hex)
- Updated docstring to reflect signed markers
All 86 tests now pass (excluding OAuth tests as specified).
The changes maintain backwards compatibility while enforcing
the new cryptographic signing requirement.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extends signed marker support to _process_system_message() for subagent
announcements. Ensures consistency across all suppress mode paths.
- Add forgery detection in system message loop
- Sign content before saving to session when suppressed
- Return unsuppressed content with suppressed metadata
- Add comprehensive tests for system message signing and forgery rejection
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Detects forged [HIDDEN:*] markers in model output and triggers rejection
with retry. Includes correction message to model and fallback stripping
if model persists. Prevents accumulation from model forgery attempts.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaces simple [HIDDEN] prefix with cryptographically signed markers
in _process_message(). Strips any forged markers from model output
before signing with system key.
Includes comprehensive accumulation test that verifies:
- Markers are properly signed when suppress_output=True
- Model seeing markers in context doesn't copy them
- No accumulation of markers across multiple messages
- Each hidden message gets exactly one signed marker
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Verifies that system prompt includes documentation about cryptographically
signed visibility markers.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Documents the purpose of [HIDDEN:{sig}] markers and explicitly forbids
model from generating them. Sets clear expectations for rejection behavior.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements verify_signature() to check HMAC validity, has_forged_marker()
to detect forgery attempts, and strip_all_hidden_markers() for cleanup.
Comprehensive test coverage for all verification scenarios.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements sign_content() to cryptographically sign message content
with HMAC-SHA256 (8-char truncated). This prevents models from forging
visibility markers as they cannot generate valid signatures.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Override SessionManager.sessions_dir to use tmp_path, preventing
session accumulation across test runs.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Use tmp_path fixture for automatic cleanup
- Assert exactly 1 heartbeat message (not > 0)
- Use test-specific session key instead of production key
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Bug discovered during integration test: suppress mode was
prefixing content AFTER saving to session, so session stored
unprefixed content while only the outbound message was prefixed.
Fix: Move suppress check before session save and use prefixed
content when adding to session messages.
test: add end-to-end integration test for idle heartbeat
Verifies complete flow:
- Idle detection triggers heartbeat
- Heartbeat runs in main session
- Output is suppressed with [HIDDEN] prefix
- Session contains full context
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add future annotations import for type hints
- Add TYPE_CHECKING import for SessionManager forward reference
- Remove unused response variable in _tick method
- Fix whitespace in docstring
Tests directory changes (removed unused imports) remain in working tree
but are not committed due to .gitignore.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Heartbeat now:
- Checks last user message timestamp in target session
- Only triggers if >30min elapsed since last user message
- Passes suppress_output metadata to callback
- Removes HEARTBEAT_OK check (unnecessary with suppress mode)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes bug where suppressed messages would leave typing indicator
running forever. Now _stop_typing() is called before early return.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Messages with metadata['suppressed']=True are logged but not sent
to Telegram API, enabling heartbeat to run without spamming user.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Allows passing metadata through process_direct() for features like
suppress mode in heartbeat.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- MessageTool now writes sent messages to session history via SessionManager
- Agent loop wires SessionManager into MessageTool constructor
- Session.get_history() returns full history (removed max_messages limit)
Server-side context editing API handles trimming, so we send full history
This ensures messages sent via the message() tool (e.g., from heartbeat forks)
are visible in the main conversational agent's context.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sending a slice of history can cut in the middle of a tool chain, causing
'unexpected tool_use_id' 400 errors when the API receives an orphaned
tool_result without its preceding assistant tool_use block.
The server-side context editing API (clear_tool_uses_20250919) handles
trimming safely at token thresholds while respecting tool chain boundaries.
Let the server manage context length; send the full history from the client.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- session/manager.py: add add_raw_message() to persist tool chain messages;
get_history() now passes all API-relevant fields (tool_calls, tool_call_id,
name, reasoning_content) instead of stripping to role+content only
- loop.py: after each turn, save the complete message sequence (tool_use,
tool_results, thinking blocks, final reply) instead of just the final text;
remove automatic consolidation trigger — server-side context editing handles
the token window now; _consolidate_memory (runs on /new) updated to handle
list content, tool messages, and new message formats
- anthropic_oauth.py: add context_management parameter to chat() and
_make_request(); log context edits applied by Anthropic; log context_mgmt
strategies in request log line
- oauth_utils.py: add context-management-2025-06-27 beta header
- base.py, litellm_provider.py: propagate context_management parameter
CONTEXT_MANAGEMENT config on every agent call:
- clear_thinking_20251015 keep="all" → preserve all thinking blocks for cache
- clear_tool_uses_20250919 trigger=80k tokens, keep=5 recent tool uses
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
SubagentManager was receiving model=self.model (Opus) from the main
agent loop, overriding the intended "claude-sonnet-4-6" fallback in
SubagentManager.__init__. Subagents should default to Sonnet unless
explicitly overridden via the spawn tool call.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Restore f-string prefix so {self.workspace} interpolates correctly
- Add task parameter back to _build_subagent_prompt signature
- Instruct subagents to run exec date as first action (avoids
injecting dynamic timestamp into system prompt that busts cache)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
MEMORY.md is updated by the Haiku consolidator after every session,
changing the system prompt and busting the 1h cache. Replace it with
KNOWLEDGE.md — a static, manually-curated file that stays stable.
MEMORY.md remains accessible to the agent via read/grep tools.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
session.add_message was storing raw msg.content without the [Current time: ...]
prefix, causing cache key mismatches on subsequent turns since the API received
the prefixed version but history replayed the raw version.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The system prompt included a minute-resolution timestamp that changed every
call, busting the 1h cache on every request. Move current time to a [Current
time: ...] prefix on each user message instead, keeping the system prompt
static for cache hits. Also clarify the time-gap notice text.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Cache last user message on every API call (5m TTL) so full conversation
history is a cache read on subsequent turns
- Skip "Reflect on the results" interleave prompt when thinking_budget > 0
since extended thinking already handles reflection internally; keeps
message caching valid across tool iterations
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Cache system prompt and tool definitions on every API call to reduce
quota burn. Uses 1-hour TTL so context stays warm across conversations.
Also logs cache_write/cache_read token counts in response log line.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Previously wait_for_subagents was only registered inside _run_subagent
(spawned orchestrators). Main conversation agent had no access to it.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Summary
- Adds time-gap awareness to `_process_message` in `nanobot/agent/loop.py`
- When >5 minutes have passed since the last user message in a session, prepends `[SYSTEM ANNOUNCEMENT: X minutes/hours/days have elapsed since last user message]` to the current message content
- Keeps the LLM aware of real time elapsed between conversation turns
## Implementation Details
- Walks `session.messages` in reverse to find the last user message timestamp
- Uses `datetime.fromisoformat()` to parse the stored ISO timestamps
- Threshold: 300s (5 min) → formats as minutes, hours, or days
- Malformed timestamps silently skipped (try/except)
- Note: `session.add_message("user", ...)` runs **after** `build_messages`, so the reversed walk finds the *previous* user message — no off-by-one issue
- Only affects `_process_message`; `_process_system_message` (subagent announces) is unchanged
## Test plan
- [ ] Send two messages with >5 min gap — second message should log with `[SYSTEM ANNOUNCEMENT: X minutes have elapsed...]` prefix
- [ ] Send two messages with <5 min gap — no prefix injected
- [ ] Verify new session (no prior messages) — no prefix injected
🤖 Generated with [Claude Code](https://claude.ai/claude-code)
Co-authored-by: code-server <code-server@wylab.me>
Reviewed-on: #10
Quota switching updates the main agent's model selection but never updates
SubagentManager.self.model, so any spawn() call without an explicit model
parameter fell back to Opus. Defaulting to Sonnet fixes this — explicit
overrides (e.g. model="claude-haiku-4-5") still take precedence.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Child subagents (origin_channel="subagent") no longer announce to Telegram;
results are stored in _task_results[task_id] instead
- New WaitForSubagentsTool: blocks via asyncio.gather until all specified
task IDs complete, returns collected results for orchestrator synthesis
- spawn() return message now includes Task ID prominently for collection
- Fixes: orchestrator spawning N workers caused N+1 Telegram messages
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Registers SpawnTool in _run_subagent so subagents can spawn child
subagents. Removes the "cannot spawn" restriction from the system prompt.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Writes rate_limits.json after every API call with weekly/5h utilization
- Writes api_headers.jsonl with raw headers for analysis
- Upgrades quota fallback model from Sonnet 4.5 to 4.6
Deploy to site-packages (gateway loads from there, not /app/):
docker cp to /usr/local/lib/python3.12/site-packages/nanobot/providers/
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Changed printf-style (%s/%d) to loguru format ({}) in 3 log statements
- Consolidated response logging into a single line showing stop_reason,
tool_calls count, thinking chars, and token usage
- Added tool count to request logging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The system message handler was using self.model instead of the
quota-selected model, bypassing the Opus/Sonnet switching logic.
Also added debug logging for model selection and thinking_budget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement intelligent model selection to manage 7-day Opus quota burn rate:
- Add _select_model_based_on_quota() method to AgentLoop
- Reads rate limit data from memory/rate_limits.json
- Calculates expected vs actual quota usage (100%/168h = 0.595% per hour)
- If actual > expected × 1.17 (17% overage), downgrades to Sonnet
- If actual ≤ expected, uses Opus
- Caches decision for 5 minutes to minimize file I/O
- Add /quota slash command to display real-time quota status
- Shows current usage vs expected usage
- Shows hours until weekly reset
- Shows selected model and burn rate multiplier
- Main agent now calls _select_model_based_on_quota() before each conversation
- Heartbeat subagent unaffected (explicitly uses claude-sonnet-4-20250514)
This replaces the wrong approach from PR #5 which throttled heartbeat
frequency instead of switching the main agent's model.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Root cause: consolidation was calling Opus 4.6 with 10k thinking budget
on 50-80 message prompts. The 300s httpx timeout killed every request
(all failures were exactly 5 minutes after start). Consolidation is just
summarization — Haiku with no thinking handles it in seconds.
Also adds per-call thinking_budget override to the provider interface
so callers can disable thinking for lightweight tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-deploy is now handled by Watchtower on Unraid, which polls
for new images every 5 minutes for labeled containers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allows triggering a deploy via Gitea API. SSHes to Unraid to pull
latest image and restart the nanobot container.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
REGISTRY env var is just the hostname without scheme. Docker actions
handle this automatically, but curl needs the full URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runs after push builds and daily at 03:00 UTC. Keeps :latest and
:buildcache, deletes old SHA-tagged images via Gitea packages API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add pull_request trigger to build workflow
- Skip push and cache-to on PRs (build-only validation)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove "# nanobot" branding and "You are nanobot" from context.py
- Remove "You are a helpful AI assistant" personality line
- Remove fake "required" Claude Code system prefix from OAuth provider
- Identity is now fully customizable via IDENTITY.md in workspace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Homebrew refuses to run as root in Docker containers.
Replace all brew installs with:
- GitHub release binaries (gogcli, goplaces, himalaya, obsidian-cli)
- go install (songsee)
- npm (gemini-cli)
- uv tool (openai-whisper)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nanobot uses loguru, not stdlib logging. Switch to loguru so
thinking/usage logs actually appear in container output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logs thinking block presence, character count, and token usage
in API responses. Also logs request parameters including thinking
budget configuration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Anthropic API returns a signature field in thinking blocks that
must be replayed in subsequent turns. Store full thinking blocks
(including signatures) instead of just the text content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of maintaining a brittle alias dict mapping model names to
dated API IDs, simply normalize dots to hyphens. The Anthropic API
accepts both claude-sonnet-4-5 and dated variants like
claude-sonnet-4-5-20250929, so no alias table is needed. This lets
users write "claude-sonnet-4.5" or "claude-sonnet-4-5" interchangeably.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds configurable thinking_budget in agent defaults. When >0, sends
the thinking parameter to the API with the specified token budget.
Handles API constraints: forces temperature=1, auto-bumps max_tokens
if it's below the thinking budget, preserves thinking blocks in
message history for multi-turn conversations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The agent loop produces messages in OpenAI format (role:tool, tool_calls
array) but the Anthropic API expects its own format (tool_use content
blocks in assistant messages, tool_result blocks in user messages).
This caused 400 errors whenever the bot tried to use tools like
web_search, because the follow-up message with tool results was
malformed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow "*" in the allowFrom list to explicitly permit all senders,
as an alternative to the empty-list-means-allow-all behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .github/workflows/build.yml to auto-build and push to Gitea registry
- Change Dockerfile.oauth CMD from "status" to "gateway" for persistent container
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MODEL_ALIASES dict to resolve short model names (e.g. claude-sonnet-4)
to dated API IDs (e.g. claude-sonnet-4-20250514). Includes claude-opus-4-6.
Add Dockerfile.oauth overlay extending birdxs/nanobot:latest for fast builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.