Introduces a unified extension abstraction over MCP servers and WASM tools
with six agent-callable tools (tool_search, tool_install, tool_auth,
tool_activate, tool_list, tool_remove) so users can add capabilities
conversationally without CLI commands.
Includes built-in registry of 11 MCP servers, online discovery via URL
probing and GitHub search, OAuth 2.1 flows for MCP servers, and manual
token auth for WASM tools.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Thread message metadata through Channel::send_status so WASM channels
can route status updates (like typing indicators) to the correct chat.
The WasmChannel spawns a background task that repeats on_status every
4 seconds to keep Telegram's typing bubble alive until the response
is sent.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Enables connecting to official MCP servers (like Notion) instead of
building custom WASM tools. Uses OAuth 2.1 with PKCE and supports
Dynamic Client Registration for zero-config authentication.
Key features:
- OAuth 2.1 flow with PKCE for secure browser-based auth
- Dynamic Client Registration (DCR) for servers without pre-configured clients
- Automatic token refresh on 401 responses
- Session management with Mcp-Session-Id headers
- SSE streaming response handling
New CLI commands:
- `mcp add <name> <url>` - Add an MCP server
- `mcp remove <name>` - Remove an MCP server
- `mcp list` - List configured servers
- `mcp auth <name>` - Authenticate with a server
- `mcp test <name>` - Test connection
Also removes the Notion WASM tool example since it's superseded by the
Notion MCP server which provides 13 official tools.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Renamed project from "near-agent" to "ironclaw" throughout the codebase.
Updated documentation to emphasize the core philosophy:
- Your data stays yours (local, encrypted, no telemetry)
- Self-expanding capabilities (build tools on the fly)
- Defense in depth (WASM sandbox, prompt injection defense)
- Always on user's side
Key changes:
- Package name: near-agent -> ironclaw
- Config paths: ~/.near-agent/ -> ~/.ironclaw/
- Database name in docs: near_agent -> ironclaw
- CLI binary: near-agent -> ironclaw
- Log filters: RUST_LOG=near_agent -> RUST_LOG=ironclaw
- All user-facing strings (welcome messages, help text, etc.)
Preserved for compatibility:
- HKDF salt "near-agent-secrets-v1" (changing would break existing secrets)
- WIT interface names (near::agent::*)
- NEAR AI provider config (NEARAI_* env vars)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The builder would get stuck when the LLM returned JSON specs or planning
text instead of tool calls. The loop would continue for all iterations
without making progress, eventually timing out.
Changes:
- Make initial prompt directive: "Use write_file NOW" instead of passive
"Start by creating the project structure"
- Add consecutive_text_responses counter to detect stuck state
- Fail fast after 2 consecutive text-only responses with clear error
- Send strong nudge on first text response: "STOP. Call write_file..."
- Reset counter once tools have been executed (completion phase)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add escape_telegram_markdown() to handle underscores in tool names
(e.g., build_software was breaking Telegram's Markdown parser)
- Use Telegram-compatible *bold* syntax instead of **bold**
- Clarify workspace memory vs filesystem tool descriptions to prevent
LLM from using read_file on memory_tree paths
- Update build_software to strongly prefer Rust WASM for agent tools
- Rewrite WASM tool template to use Component Model with wit_bindgen
instead of outdated extern "C" approach
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The WASM channel no longer needs its own polling_enabled/tunnel_url settings.
Instead, the host injects tunnel_url and webhook_secret into the channel config
at runtime before start() is called.
Changes:
- Add update_config() method to WasmChannel for runtime config injection
- Simplify TelegramConfig to only have bot_username, respond_to_all_group_messages
- Host injects tunnel_url (from Settings) and webhook_secret (from secrets store)
- Channel checks if tunnel_url is present to determine webhook vs polling mode
- Add delete_webhook() for clean transition to polling mode when no tunnel
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implements the sandboxed-channel WIT interface for WhatsApp Cloud API:
- Webhook verification (GET with hub.mode=subscribe)
- Incoming message handling (POST webhooks)
- Outgoing responses via Graph API
- Parses WhatsApp webhook payload format
Built and tested in Docker sandbox with wasm32-wasip2 target.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Remove the brittle natural language pattern matching from the router
and add job management tools to the normal tool registry instead.
- Add job tools: create_job, list_jobs, job_status, cancel_job
- Router now only handles explicit /commands
- Natural language goes through agentic loop with all tools
- LLM naturally picks appropriate tools based on user intent
- Share ContextManager between job tools and Agent
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The test expected "Can you create a website for me?" to route as CreateJob,
but the extract_intent logic intentionally requires explicit job creation
patterns (containing both "create" and "job") to avoid capturing general
conversation as job requests.
Updated test to verify:
- "create job: ..." routes to CreateJob
- Messages with both "create" and "job" route to CreateJob
- General requests without explicit "job" fall through to Chat
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implements a general-purpose Docker sandbox (inspired by Codex) that provides:
- Container isolation for shell commands with ephemeral containers
- HTTP proxy for network access control with domain allowlist
- Credential injection by proxy (secrets never enter containers)
- Three security policies: ReadOnly, WorkspaceWrite, FullAccess
- Resource limits (memory, CPU, timeout enforcement)
Key components:
- SandboxManager: Main entry point coordinating proxy and containers
- NetworkProxy: HTTP proxy validating requests and injecting credentials
- ContainerRunner: Docker lifecycle management via bollard
- DomainAllowlist: Pattern matching for allowed network destinations
The ShellTool now routes commands through the sandbox when enabled,
with automatic fallback to direct execution if Docker is unavailable.
Configuration via SANDBOX_ENABLED, SANDBOX_POLICY, SANDBOX_TIMEOUT_SECS,
SANDBOX_MEMORY_LIMIT_MB, SANDBOX_IMAGE, SANDBOX_EXTRA_DOMAINS env vars.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Enable instant message delivery for Telegram via webhooks instead of polling.
Key changes:
- Add tunnel URL configuration for local development (ngrok, cloudflare)
- Auto-register webhook with Telegram API on startup using setWebhook
- Implement webhook secret validation via X-Telegram-Bot-Api-Secret-Token header
- Add credential injection for bot token via URL placeholder substitution
- Fix metadata preservation in respond() to route replies correctly
- Fix serde flatten with Option<T> issue in capabilities schema parsing
The credential injection pattern replaces {TELEGRAM_BOT_TOKEN} placeholders
in URLs with the actual token from the secrets store, keeping credentials
out of WASM module memory until the HTTP request is made.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Changes setup wizard to save channel secrets (Telegram bot token, HTTP
webhook secret) to PostgreSQL via SecretsStore instead of files.
This enables the WASM channel credential injector to find and inject
the secrets properly, since it reads from the database.
Requires:
- DATABASE_URL to be set
- SECRETS_MASTER_KEY (will generate and display if not set)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduces `near-agent setup` command that guides users through:
- NEAR AI authentication (reuses existing OAuth flow)
- Model selection (fetches from API or shows defaults)
- Channel configuration (HTTP webhook, Telegram)
Features:
- First-run detection: auto-runs wizard if no session exists
- Respects existing settings: shows current model with keep/change option
- Saves channel secrets to ~/.near-agent/secrets/ with 0600 permissions
- Validates Telegram bot tokens via API before saving
Also fixes default NEARAI_BASE_URL to use cloud-api.near.ai (api.near.ai
returns 410 Gone).
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implements a loadable WASM channel for Telegram following the existing
Slack channel pattern:
- Webhook-based message receiving at /webhook/telegram
- Private chat and group chat support (with @mention filtering)
- Reply threading via reply_to_message_id
- User name extraction from Telegram user objects
- Bot token injection by host (never exposed to WASM)
Files:
- channels-src/telegram/src/lib.rs - Main implementation
- channels-src/telegram/Cargo.toml - Dependencies
- channels-src/telegram/telegram.capabilities.json - Permissions
- channels-src/telegram/build.sh - Build script
To use: copy telegram.wasm and telegram.capabilities.json to
~/.near-agent/channels/ and configure telegram_bot_token secret.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Comprehensive comparison of IronClaw vs OpenClaw features to enable
coordinated development. Includes status indicators, priority levels,
owner fields for claiming work, and documented architectural deviations.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add NearAiChatProvider using /v1/chat/completions endpoint with API key auth
- Add NEARAI_API_KEY and NEARAI_API_MODE config options
- Auto-detect API mode from presence of API key
- Keep existing Responses API (NearAiProvider) for session-based auth
- Fix response parsing to accept input_text/output_text/text content types
- Expand REPL with /help, /debug toggle, colored output
- Better tool status display (dots vs verbose based on debug mode)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit addresses three critical issues from code review:
1. Tool approval enforcement: Tools declaring requires_approval() (shell,
http, file write/patch, build_software) now gate execution. Adds
PendingApproval struct, session-scoped auto-approved tools set, and
approval flow with yes/no/always commands.
2. Tool definition refresh: Tool definitions now refresh each iteration
in both chat and job loops, so newly built tools become visible
immediately within the same session.
3. Worker tool call handling: Changed respond() to respond_with_tools()
when select_tools returns empty, properly executing tool calls instead
of formatting them as text.
Also includes prior work from the plan:
- Wire embeddings provider (OpenAI + NEAR AI) to workspace
- Load workspace system prompt (identity files) into LLM context
- Route heartbeat notifications through channel manager
- Enable auto-context compaction when threshold exceeded
- Refactor to config structs (AgentDeps, WorkerDeps, LlmCallRecord)
- Fix clippy warnings (saturating_sub, too_many_arguments)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add HeartbeatConfig for proactive periodic execution with channel notifications
- Add use_planning option to Worker for ActionPlan generation before tool execution
- Implement tool failure tracking in database (V3 migration)
- Add auto-repair via Builder for broken WASM tools in self_repair.rs
- Record tool failures in Worker for self-repair tracking
- Update .env.example with new configuration options
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Switch from List to Paragraph widget for message display
- Split message content by newlines and render each line separately
- Auto-scroll to show most recent messages
- Add empty line between messages for readability
- Wrap long lines properly
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The API returns a different JSON structure with output as a Value.
Added extract_text_from_output() to handle various output formats:
- String: return directly
- Array with text/content fields: extract and join
- Object with text/content/message fields: extract
Both complete() and complete_with_tools() now handle this format.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The API sometimes returns plain text instead of JSON structure.
Now we detect this and extract the text content directly.
- Detect "Unexpected response format" errors with raw text
- Parse as JSON string if quoted, otherwise use raw
- Works for both complete() and complete_with_tools()
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove EnableMouseCapture/DisableMouseCapture from terminal setup
- Users can now select and copy text using normal terminal selection
- We weren't using mouse events anyway
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Log request body before sending
- Log full response text before parsing
- Log parse errors with full response content
- This helps debug why output array is empty
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add direct text field support on NearAiOutputItem
- Accept both "output_text" and "text" content types
- Join all content items instead of taking just the first
- Better debug logging showing item.text field
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add ThinkingMessage variant to AppEvent for chat-visible status
- Add set_thinking() and clear_thinking() to AppState
- Thinking messages show as system messages with spinner indicator
- Auto-clear thinking messages when agent response arrives
- Tool started/completed now shown in chat window
- Add debug logging to NEAR AI provider to diagnose empty responses
- Log raw response output when text extraction returns empty
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add 2 minute timeout to HTTP client for LLM calls
- Add debug logging for requests and error logging for failures
- Prevents indefinite hangs when NEAR AI is slow or unavailable
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add StatusUpdate enum with Thinking, ToolStarted, ToolCompleted, StreamChunk, Status variants
- Add send_status method to Channel trait (default no-op)
- Implement send_status in TuiChannel to show status in UI
- Add send_status to ChannelManager for routing to specific channels
- Update handle_message to send "Processing..." status for Chat/CreateJob
- Update handle_chat to send "Generating response..." and show errors
Now when a user sends a message, they see feedback that the agent is working.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduces a new CLI module with subcommands for managing WASM tools:
- `tool install`: Build and install tools from source or .wasm files
- `tool list`: List installed tools with optional verbose output
- `tool remove`: Remove installed tools
- `tool info`: Show detailed tool information including capabilities
The install command supports building from Cargo source directories using
cargo-component, or installing pre-compiled .wasm files directly. It
auto-detects capabilities JSON sidecar files and validates them before
installation.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
When the TUI quits (Ctrl+D twice), it now:
1. Sends a "/shutdown" message through the channel before closing
2. Explicitly drops msg_tx to ensure channel closure
The agent loop now:
1. Returns Option<String> from handle_message (None = shutdown)
2. Handles /quit, /exit, /shutdown commands by returning None
3. Breaks out of the main loop on shutdown signal
4. Lists /quit in help menu
Co-Authored-By: Claude Opus 4.5 <[email protected]>