# Notes & Tool Execution Framework — Implementation **Version:** 0.7.2 (Phase 2 per ARCHITECTURE.md §10) **Depends on:** channels (006), messages with tree (013) — both deployed --- ## Status: Complete ### Migration - [x] `014_notes.sql` — notes table, tsvector full-text search, folder/tag indexes, auto-update trigger ### Notes API (handlers/notes.go) - [x] `POST /notes` — create note with title, content, folder, tags, source_channel_id - [x] `GET /notes` — list with folder/tag filter, pagination - [x] `GET /notes/:id` — get single note - [x] `PUT /notes/:id` — update with replace/append/prepend modes - [x] `DELETE /notes/:id` — delete - [x] `GET /notes/search?q=` — full-text search with ts_rank + ts_headline - [x] `GET /notes/folders` — list distinct folder paths with counts ### Tool Type System (tools/types.go) - [x] `ToolDef` — name, description, JSON Schema parameters (sent to LLM) - [x] `ToolCall` — ID, name, arguments (from LLM response) - [x] `ToolResult` — tool_call_id, name, content, is_error (fed back) - [x] `Tool` interface — `Definition()` + `Execute(ctx, execCtx, argsJSON)` - [x] `ExecutionContext` — userID, channelID for scoped execution - [x] JSON Schema helpers: `Prop`, `PropEnum`, `PropArray`, `JSONSchema` ### Tool Registry (tools/registry.go) - [x] `Register(Tool)` — global registry, called from init() - [x] `Get(name)` — lookup by name - [x] `AllDefinitions()` — all registered tool schemas for LLM requests - [x] `ExecuteCall()` — runs a single tool, returns ToolResult (never errors) - [x] `ExecuteAll()` — batch execution - [x] `HasTools()` — check if any tools registered ### Note Tools (tools/notes.go) - [x] `note_create` — create with title, content, folder, tags; links source_channel_id - [x] `note_search` — full-text search with relevance ranking and excerpts - [x] `note_update` — update by note_id with replace/append/prepend modes - [x] `note_list` — list by folder/tag filter - All registered via init() ### Provider Tool Calling Support - [x] `providers.Message` — extended with ToolCalls, ToolCallID, Name fields - [x] `providers.CompletionRequest` — Tools field for function definitions - [x] `providers.CompletionResponse` — ToolCalls field for function calls - [x] `providers.StreamEvent` — ToolCalls accumulated on final event - [x] OpenAI provider — full tool_calls in request/response/stream - [x] Anthropic provider — tool_use/tool_result content blocks, input_json_delta streaming, consecutive user message merging - [x] OpenRouter — inherits from OpenAI (delegation) - [x] Venice — inherits from OpenAI (delegation) ### Completion Handler Tool Loop (handlers/completion.go) - [x] `buildToolDefs()` — converts registered tools to provider format - [x] Tools attached when `caps.ToolCalling && tools.HasTools()` - [x] Streaming: tool loop with max 10 iterations - Stream text deltas normally - On `finish_reason: "tool_calls"`: execute tools, send SSE events - Custom SSE events: `event: tool_use` and `event: tool_result` - Re-call provider with tool results appended to messages - Final text response streamed normally - [x] Non-streaming: same loop, tools executed synchronously - [x] Safety: `maxToolIterations = 10` prevents infinite loops --- ## SSE Events for Tool Execution The streaming completion sends custom named SSE events during tool execution. Standard data events continue to carry OpenAI-compatible content deltas. | Event | Payload | When | |-------|---------|------| | `tool_use` | `[{"id":"...","type":"function","function":{"name":"note_create","arguments":"..."}}]` | LLM requests tool calls | | `tool_result` | `{"tool_call_id":"...","name":"note_create","content":"...","is_error":false}` | Tool execution complete | The frontend can listen for these to show tool execution status. Existing frontends that only handle `data:` events are unaffected. --- ## Design Decisions 1. **Separate packages** — `tools/` is independent of `handlers/`. Tools don't know about HTTP. The completion handler bridges them. 2. **init() registration** — Tools self-register. Adding a new tool = create file with init(), done. No central wiring. 3. **JSON Schema parameters** — Tool definitions use standard JSON Schema, matching OpenAI's function calling spec. 4. **Non-streaming tool loop** — After tool execution, re-calls use streaming. Each iteration streams text deltas. 5. **Anthropic content blocks** — Full translation: `tool_use` blocks, `tool_result` as user messages, consecutive user message merging for alternating-role requirement. 6. **Folder paths** — Normalized to `/path/` format. Root is `/`. Tree structure without a folders table. 7. **Search** — PostgreSQL tsvector with weighted ranks (title=A, content=B). No external search infrastructure needed. --- ## Frontend Work - [x] Notes panel/modal in UI - [x] Notes CRUD forms (create, edit, list, search) - [x] Tool execution indicators during streaming - [x] Parse `event: tool_use` / `event: tool_result` SSE events - [x] Display tool activity in message bubbles