# DESIGN-0.13.1 — Web Search + URL Fetch + Tool Toggle

## Overview

Built-in `web_search` and `url_fetch` tools using the existing tool framework (v0.11.0),
plus a chat-bar tools toggle menu so users can enable/disable tool categories per-session.

Depends on: tool framework (v0.11.0), admin panel (v0.13.0).

## Tools

### `web_search`

**Provider abstraction** — same pattern as providers. Default: DuckDuckGo. Admin adds
SearXNG instances or other search APIs.

**Model:** `searchResult[]` — title, url, snippet.

```json
{
  \"tool_name\": \"web_search\",
  \"parameters\": {
    \"query\": \"string\"  // required
  }
}
```

**Backend:** Provider interface, DuckDuckGo fallback. Results deduped by URL.
Max 10 results (configurable). Results cached in-memory 1h (channel-scoped).

### `url_fetch`

**Model:** `webPage` — title, url, content (HTML or text).

```json
{
  \"tool_name\": \"url_fetch\",
  \"parameters\": {
    \"url\": \"string\"  // required, validated
  }
}
```

**Backend:** HTTP GET with timeout (10s), content-type sniffing, HTML→text
conversion (go-readability). Max 100KB extracted text. Cached 1h.

## Tool Categories + Toggle UI

**Categories** (backend enum):
- `web` — web_search, url_fetch
- `code` — code_exec (future)
- `kb` — kb_search (v0.14.0)
- `memory` — memory_recall (v0.18.0)

**Per-chat persistence:** `channel.settings.tools[]` array of enabled categories.
Default: all.

**Chat bar UI:** Toggle icon → dropdown with checkboxes per category.
Syncs to API on change (`PATCH /channels/:id {tools: [...]}`).

**Admin global default:** `global_settings.default_tools[]`.

## Backend Changes

### 1. `server/tools/search.go` — New file

```go
// web_search + url_fetch providers + caching
```

### 2. `server/tools/registry.go` — Category enum + filtering

```go
type ToolCategory string

const (
    CategoryWeb  ToolCategory = \"web\"
    CategoryCode ToolCategory = \"code\"
    // ...
)

func (t *Tool) Category() ToolCategory
```

CompletionHandler filters available tools by channel.settings.tools.

### 3. `server/handlers/channels.go` — PATCH tools[]

```go
case \"tools\":
    if tools, ok := data.([]string); ok {
        ch.Tools = tools
    }
```

### 4. `server/config/config.go` — Global defaults

```go
DefaultTools []string `json:\"default_tools\"`
```

### 5. `VERSION`

```
0.13.1
```

## Frontend Changes

### `src/js/chat-ui.js` — Tool toggle button

After model selector (~line 450):

```javascript
// Tool toggle dropdown
const toolToggle = document.createElement('div');
toolToggle.className = 'tool-toggle';
toolToggle.innerHTML = `
  <button class=\"tool-btn\" onclick=\"ChatUI.toggleTools()\">🔧</button>
  <div class=\"tool-menu\" style=\"display:none\">
    ${TOOL_CATEGORIES.map(cat => 
      `<label><input type=\"checkbox\" data-cat=\"${cat}\" checked> ${cat}</label>`
    ).join('')}
  </div>
`;
chatBar.appendChild(toolToggle);
```

### Event handler

```javascript
ChatUI.toggleTools = function() {
  const menu = document.querySelector('.tool-menu');
  menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
};

// Checkbox change → API PATCH + refresh available tools
document.querySelectorAll('[data-cat]').forEach(cb => {
  cb.addEventListener('change', async function() {
    const tools = Array.from(document.querySelectorAll('[data-cat]:checked'))
      .map(cb => cb.dataset.cat);
    await API.updateChannel(channelId, { tools });
    ChatUI.refreshTools(); // filter tool_calls display
  });
});
```

## Testing Checklist

1. **Tools register** — logs show `🔧 Registered tool: web_search`
2. **web_search works** — DuckDuckGo fallback, results in tool_calls
3. **Admin search provider** — add SearXNG, verify switch
4. **url_fetch** — valid URL → content extracted
5. **Toggle UI** — checkbox → tools filtered from completion
6. **Persistence** — reload chat → toggles restored
7. **Admin default** — new chat inherits global default_tools

## Architecture Notes

- **No tool auth** — web_search/url_fetch are anon-safe
- **Caching** — channel-scoped LRU (100 entries), 1h TTL
- **Rate limiting** — 5/min per channel (global_settings.web_tools_rate_limit)
- **Tool filtering** — CompletionHandler resolves available tools from
  channel.tools + global default_tools intersection with registered tools' categories
- **Provider symmetry** — search providers mirror LLM providers (health, priority)